WinDriver V5 User's Guide

COPYRIGHT

Copyright © 1997-2001 Jungo Ltd. All Rights Reserved

Information in this document is subject to change without notice. The software described in this document is furnished under a license agreement. The software may be used, copied or distributed only in accordance with that agreement. No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or any means, electronically or mechanical, including photocopying and recording for any purpose without the written permission of Jungo Ltd.

Windows, Win32, Windows 95, Windows 98, Windows ME, Windows CE, Windows NT and Windows 2000 are trademarks of Microsoft Corp. WinDriver and KernelDriver are trademarks of Jungo. Other brand and product names are trademarks or registered trademarks of their respective holders.

Contents

1  WinDriver Overview
    1.1  Introduction to WinDriver
    1.2  Background
    1.3  WinDriver Advantages
    1.4  WinDriver Feature List
    1.5  WinDriver Architecture
    1.6  What Platforms does WinDriver Support?
    1.7  Can I Try WinDriver Before I Buy?
    1.8  Limitations of the different evaluation versions
    1.9  How do I develop my Driver with WinDriver? (Overview)
        1.9.1  On Windows 95, 98, ME, NT and 2000
        1.9.2  On Windows CE
        1.9.3  On Linux and Solaris
        1.9.4  On Embedded Operating Systems
    1.10  What Does the WinDriver Toolkit Include?
        1.10.1  WinDriver Modules
        1.10.2  Utilities
        1.10.3  WinDriver's SPECIFIC CHIP-SET SUPPORT
        1.10.4  Samples
    1.11  Can I distribute the driver created with WinDriver?
    1.12  Device Driver Overview
        1.12.1  Monolithic Drivers
        1.12.2  Windows 95/98/ME Drivers
        1.12.3  NT Driver Model
            1.12.3.1  Layered Drivers
            1.12.3.2  Miniport Drivers
        1.12.4  Unix Device Drivers
        1.12.5  Linux Device Drivers
        1.12.6  Solaris Device Drivers
    1.13  Matching the right tool for your driver
2  WinDriver USB Overview
    2.1  Introduction to USB
    2.2  Feature List
    2.3  USB Components
    2.4  Data Flow in USB Devices
    2.5  USB Data Exchange
    2.6  USB Data Transfer Types
    2.7  USB Configuration
    2.8  WinDriver USB
    2.9  WinDriver USB Architecture
    2.10  What drivers can I write with WinDriver USB?
3  Installation and Setup
    3.1  Systems Requirements
        3.1.1  For Windows 95 / 98 / ME
        3.1.2  For Windows NT/2000
        3.1.3  For Windows CE
        3.1.4  For Linux
        3.1.5  For Solaris
        3.1.6  For VxWorks
    3.2  Installing WinDriver
        3.2.1  Installing WinDriver for Windows 95,98,ME,NT and 2000
        3.2.2  Installing WinDriver CE
            3.2.2.1  Installing WinDriver CE for a hand held PC
            3.2.2.2  Using WinDriver CE with ETK/Platform Builder
        3.2.3  Testing Your Applications on the X86 HPC Emulator
            3.2.3.1  Emulation on Windows NT
        3.2.4  Installing WinDriver for Linux
            3.2.4.1  Restricting hardware access on Linux
        3.2.5  Installing DriverWizard on your Windows machine
        3.2.6  Installing WinDriver for Solaris
            3.2.6.1  Restricting hardware access on Solaris
            3.2.6.2  Solaris Platform Specific Issues
        3.2.7  Installing DriverBuilder for VxWorks
    3.3  Upgrading Your Installation
    3.4  Checking Your Installation
        3.4.1  On your Windows machine
        3.4.2  On your Windows CE machine
        3.4.3  On your Linux machine
        3.4.4  On your Solaris machine
        3.4.5  On VxWorks
    3.5  Uninstalling WinDriver
        3.5.1  Uninstalling Windriver from Windows (95/98/ME/NT/2000)
        3.5.2  Uninstalling Windriver from Linux
        3.5.3  Uninstalling WinDriver from Solaris
        3.5.4  Uninstalling DriverBuilder for Vxworks
4  The DriverWizard
    4.1  An Overview
    4.2  DriverWizard Walkthrough
    4.3  DriverWizard Notes
        4.3.1  Sharing a Resource
        4.3.2  Disabling a Resource
        4.3.3  DriverWizard Logger
        4.3.4  Automatic Code Generation
    4.4  Remote WinDriver
        4.4.1  How does it work?
        4.4.2  Using Remote WinDriver (For all Windows OSes)
        4.4.3  Using Remote WinDriver (For Windows CE)
        4.4.4  Using Remote WinDriver (For Linux and Solaris)
        4.4.5  Using Remote WinDriver (For VxWorks)
5  Creating Your Driver
    5.1  Using the DriverWizard to Build a Device Driver
    5.2  Writing the Device Driver without the DriverWizard
    5.3  Win CE - Testing on CE
    5.4  Using the Help Files
6  Debugging
    6.1  User Mode Debugging
    6.2  DebugMonitor
        6.2.1  Using DebugMonitor
            6.2.1.1  DebugMonitor - Graphical Mode
            6.2.1.2  DebugMonitor - Console Mode
            6.2.1.3  DebugMonitor on Linux and Solaris
            6.2.1.4  DebugMonitor on Windows CE
            6.2.1.5  DebugMonitor on VxWorks
7  WinDriver Function Reference
    7.1  WD_Open()
    7.2  WD_Close()
    7.3  WD_Version()
    7.4  WD_PciScanCards()
    7.5  WD_PciGetCardInfo()
    7.6  WD_PciConfigDump()
    7.7  WD_PcmciaScanCards()
    7.8  WD_PcmciaGetCardInfo()
    7.9  WD_PcmciaConfigDump()
    7.10  WD_IsapnpScanCards()
    7.11  WD_IsapnpGetCardInfo()
    7.12  WD_IsapnpConfigDump()
    7.13  WD_CardRegister()
    7.14  WD_CardUnregister()
    7.15  WD_Transfer()
    7.16  WD_MultiTransfer()
    7.17  WD_IntEnable()
    7.18  WD_IntDisable()
    7.19  WD_IntWait()
    7.20  WD_IntCount()
    7.21  WD_DMALock()
    7.22  WD_DMAUnlock()
    7.23  WD_Sleep()
    7.24  WD_UsbScanDevice()
    7.25  WD_UsbGetConfiguration()
    7.26  WD_UsbDeviceRegister()
    7.27  WD_UsbDeviceUnregister()
    7.28  WD_UsbTransfer()
    7.29  WD_UsbResetPipe()
    7.30  InterruptThreadEnable()
    7.31  InterruptThreadDisable()
8  WinDriver Structure Reference
    8.1  WD_TRANSFER
    8.2  WD_DMA
    8.3  WD_DMA_PAGE
    8.4  WD_INTERRUPT
    8.5  WD_VERSION
    8.6  WD_CARD_REGISTER
    8.7  WD_CARD
    8.8  WD_ITEMS
    8.9  WD_SLEEP
    8.10  WD_PCI_SLOT
    8.11  WD_PCI_ID
    8.12  WD_PCI_SCAN_CARDS
    8.13  WD_PCI_CARD_INFO
    8.14  WD_PCI_CONFIG_DUMP
    8.15  WD_ISAPNP_CARD_ID
    8.16  WD_ISAPNP_CARD
    8.17  WD_ISAPNP_SCAN_CARDS
    8.18  WD_ISAPNP_CARD_INFO
    8.19  WD_ISAPNP_CONFIG_DUMP
    8.20  WD_PCMCIA_SLOT
    8.21  WD_PCMCIA_ID
    8.22  WD_PCMCIA_SCAN_CARDS
    8.23  WD_PCMCIA_CARD_INFO
    8.24  WD_ PCMCIA_CONFIG_DUMP
    8.25  WD_USB_ID
    8.26  WD_USB_PIPE_INFO
    8.27  WD_USB_CONFIG_DESC
    8.28  WD_USB_INTERFACE_DESC
    8.29  WD_USB_ENDPOINT_DESC
    8.30  WD_USB_INTERFACE
    8.31  WD_USB_CONFIGURATION
    8.32  WD_USB_HUB_GENERAL_INFO
    8.33  WD_USB_DEVICE_GENERAL_INFO
    8.34  WD_USB_DEVICE_INFO
    8.35  WD_USB_SCAN_DEVICES
    8.36  WD_USB_TRANSFER
    8.37  WD_USB_DEVICE_REGISTER
    8.38  WD_USB_RESET_PIPE
9  WinDriver Enhanced Support for Specific PCI Chip Sets
    9.1  Overview
    9.2  What is the PCI Diagnostics program?
    9.3  Using your PCI chip-set Diagnostics program
        9.3.1  Introduction
        9.3.2  Main Menu Options
            9.3.2.1  Scan PCI bus
            9.3.2.2  Locate / Choose your board
            9.3.2.3  PCI configuration registers
            9.3.2.4  Your PCI local registers
            9.3.2.5  Access memory ranges on the board
            9.3.2.6  Enable / Disable Interrupts
            9.3.2.7  Access EEPROM device (Where available)
            9.3.2.8  Pulse Local Reset (where available)
    9.4  Creating your driver without using the PCI diagnostics code
    9.5  WinDriver's specific PCI chip-set API Function Reference
        9.5.1  xxx_CountCards ()
    9.6  xxx_Open()
    9.7  xxx_Close()
    9.8  xxx_IsAddrSpaceActive()
    9.9  xxx_GetRevision()
    9.10  xxx_ReadReg ()
    9.11  xxx_WriteReg ()
    9.12  xxx_ReadSpaceByte()
    9.13  xxx_ReadSpaceWord()
    9.14  xxx_ReadSpaceDWord()
    9.15  xxx_WriteSpaceByte()
    9.16  xxx_WriteSpaceWord()
    9.17  xxx_WriteSpaceDWord()
    9.18  xxx_ReadSpaceBlock()
    9.19  xxx_WriteSpaceBlock()
    9.20  xxx_ReadByte()
    9.21  xxx_ReadWord()
    9.22  xxx_ReadDWord()
    9.23  xxx_WriteByte()
    9.24  xxx_WriteWord()
    9.25  xxx_WriteDWord()
    9.26  xxx_ReadBlock()
    9.27  xxx_WriteBlock()
    9.28  xxx_IntIsEnabled()
    9.29  xxx_IntEnable()
    9.30  xxx_IntDisable()
    9.31  xxx_DMAOpen()
    9.32  xxx_DMAClose()
    9.33  xxx_DMAStart()
    9.34  xxx_IsDMADone()
    9.35  xxx_PulseLocalReset()
    9.36  xxx_EEPROMRead()
    9.37  xxx_EEPROMWrite()
    9.38  xxx_ReadPCIReg ()
    9.39  xxx_WritePCIReg()
10  WinDriver Implementation Issues
    10.1  Performing DMA
        10.1.1  Scatter/Gather DMA
            10.1.1.1  Scatter/Gather DMA for buffers larger than 1MB
        10.1.2  Contiguous Buffer DMA
    10.2  Handling Interrupts
        10.2.1  General - Handling an Interrupt
        10.2.2  Simplified interrupt handling using windrvr_int_thread.h
        10.2.3  ISA / EISA and PCI interrupts
        10.2.4  Interrupts in Windows CE
        10.2.5  PCMCIA interrupts in Windows CE
    10.3  USB Control Transfers
        10.3.1  USB Data Exchange
        10.3.2  More about the Control Transfer
        10.3.3  The Setup Packet
        10.3.4  USB Setup Packet Format
        10.3.5  Standard device requests codes
        10.3.6  Setup Packet example
    10.4  Performing Control Transfers with WinDriver
        10.4.1  Control Transfers with DriverWizard
        10.4.2  Control Transfers with WinDriver API
11  Improving Performance
    11.1  Improving performance of your device driver - Overview
    11.2  Performance improvement checklist
    11.3  Improving the performance of your User Mode driver
        11.3.1  Using direct access to memory mapped regions
        11.3.2  Accessing IO mapped regions
12  Kernel PlugIn
    12.1  Background
    12.2  Do I need to write a Kernel PlugIn?
    12.3  What kind of Performance can I expect
    12.4  Overview of the development process
13  WinDriver Kernel PlugIn Architecture
    13.1  Introduction
    13.2  WinDriver Kernel and Kernel Plugin Interaction
    13.3  Kernel Plugin Components
    13.4  Kernel PlugIn Event Sequence
14  Kernel PlugIn - How it works
    14.1  Minimal Requirements for creating a Kernel PlugIn
    14.2  Directory structure and sample for the WinDriver Kernel PlugIn
    14.3  Kernel PlugIn implementation
        14.3.1  Before you begin
        14.3.2  Write your KP_INIT() function
        14.3.3  Write your KP_OPEN() function
        14.3.4  Write the remaining PlugIn call-backs
    14.4  KPTest - A sample Kernel PlugIn Driver
    14.5  Handling Interrupts in the Kernel PlugIn
        14.5.1  Interrupt handling in user mode (without Kernel PlugIn)
        14.5.2  Interrupt handling in the Kernel (with the Kernel PlugIn)
    14.6  Message passing
15  Writing a Kernel PlugIn
    15.1  Determining whether a Kernel PlugIn is needed
    15.2  Preparing the User Mode source code
    15.3  Creating a new Kernel PlugIn Project
    15.4  Creating a handle to the WinDriver Kernel PlugIn
    15.5  Interrupt Handling in the Kernel PlugIn
    15.6  I/O handling in the Kernel PlugIn
    15.7  Compiling your Kernel PlugIn Driver
    15.8  Installing your Kernel PlugIn Driver
16  Kernel PlugIn Function reference
    16.1  User Mode functions
        16.1.1  WD_KernelPlugInOpen()
        16.1.2  WD_KernelPlugInClose()
        16.1.3  WD_KernelPlugInCall()
        16.1.4  WD_IntEnable()
    16.2  Kernel functions
        16.2.1  KP_Init()
        16.2.2  KP_Open()
        16.2.3  KP_Close()
        16.2.4  KP_Call()
        16.2.5  KP_IntEnable()
        16.2.6  KP_IntDisable()
        16.2.7  KP_IntAtIrql()
        16.2.8  KP_IntAtDpc()
17  Kernel PlugIn structure reference
    17.1  User Mode structures
        17.1.1  WD_KERNEL_PLUGIN
        17.1.2  WD_INTERRUPT
        17.1.3  WD_KERNEL_PLUGIN_CALL
    17.2  Kernel Mode structures
        17.2.1  KP_INIT
        17.2.2  KP_OPEN_CALL
18  Developing in Visual Basic and Delphi
19  Trouble-Shooting
    19.1  WD_Open() (or xxx_Open()) fails.
    19.2  WD_CardRegister() fails
    19.3  Can't open USB device using the DriverWizard
    19.4  Can't get interfaces for USB devices.
    19.5  PCI Card has no resources when using the DriverWizard
    19.6  Computer hangs on interrupt
20  Dynamically loading your driver
    20.1  Windows NT/2000 and 95/98/ME
        20.1.1  Dynamic loading - background
        20.1.2  Why do you need a dynamically loadable driver?
        20.1.3  Dynamically loading and unloading your driver
        20.1.4  Dynamically loading your Kernel PlugIn
    20.2  Linux
    20.3  Solaris
21  Distributing your Driver
    21.1  Get a valid license for your WinDriver
    21.2  Windows 95/98/ME and NT/2000
    21.3  Creating a .INF file
        21.3.1  Why should I create an INF file?
        21.3.2  How do I install an INF file when no driver exists?
        21.3.3  How do I replace an existing driver using the INF file?
            21.3.3.1  Windows 2000
            21.3.3.2  Windows 95/98/ME
    21.4  Windows CE
    21.5  Linux
        21.5.1  WinDriver Kernel Module
        21.5.2  Your User Mode Driver
        21.5.3  Kernel Plugin Modules
        21.5.4  Installation script
    21.6  Solaris
        21.6.1  Installation script
    21.7  VxWorks
A  PC-Based Development Platform Parallel Port Cable Info
B  Limitations on demo versions
C  Version history list
D  Purchasing WinDriver
E  Distributing your driver - legal issues

List of Figures

    1.1  WinDriver Architecture
    1.2  Monolithic Drivers
    1.3  Layered Drivers
    1.4  Miniport Drivers

    2.1  USB Endpoints
    2.2  USB Pipes
    2.3  WinDriver USB Architecture

    4.1  Selection of PnP Device
    4.2  USB Device Configuration
    4.3  A PCI Diagnostics Screen
    4.4  USB Diagnostics Screen
    4.5  Generate Code Option
    4.6  Select Driver Type
    4.7  Options for Generating Code
    4.8  Remote WinDriver on all Windows OS
    4.9  Remote WinDriver on Windows CE
    4.10  Remote WinDriver on Linux

    6.1  Start DebugMonitor
    6.2  Set Trace Options

    10.1  USB Data Exchange
    10.2  USB Read and Write

    13.1  KernelPlugIn Architecture

    14.1  Interrupt Handling without Kernel PlugIn
    14.2  Interrupt Handling with the Kernel PlugIn

Chapter 1
WinDriver Overview

In this Chapter you will explore the uses of WinDriver, and learn the basic steps of creating your driver.

1.1  Introduction to WinDriver

WinDriver is a device driver development toolkit that dramatically simplifies the very difficult task of developing a device driver. The driver you develop using WinDriver is source code compatible between all supported operating systems (WinDriver currently supports Windows 95/98/ME/NT/2000/CE, Linux, Solaris, VxWorks and OS/2.). It is binary compatible between Windows 95, 98, ME, NT and 2000. Bus architecture support includes PCI/PCMCIA/ ISA/ISA PnP/EISA/CompactPCI and USB. WinDriver provides a complete solution for creating high performance drivers, which handle interrupts and I/O at optimal rates.

Don't let the size of this manual fool you - WinDriver makes developing device drivers an easy task that takes hours instead of months. Most developers will find that reading this chapter and glancing through the DriverWizard and function reference chapters is all they need to successfully write their driver.

The major part of this manual deals with the features that WinDriver offers to the advanced user.

WinDriver supports all PCI bridges, from all vendors. Enhanced support is offered for the PLX / Altera / Galileo / QuickLogic / PLDA / AMCC and V3 PCI chips. A special chapter is dedicated to developers of PCI card drivers who are using PCI chips from these vendors. The last several chapters of this manual explain how to tune your driver code to achieve optimal performance. The ``Kernel PlugIn'' feature of WinDriver is explained there. This feature allows the developer to write and debug the entire device driver in the User Mode, and later `drop' performance critical parts of it into the Kernel Mode. Therefore, the driver achieves optimal Kernel Mode performance, with User Mode ease of use.

Please check Jungo's web site at http://www.jungo.com for the latest news about WinDriver and other driver development tools that Jungo offers.

Good luck with your project!

1.2  Background

In protected operating systems (such as Windows, Linux, Solaris and OS/2), a programmer cannot access hardware directly from the application level (the ``User Mode'') where development work is usually done. Hardware access is allowed only from within the operating system itself (the ``Kernel Mode'' or ``Ring 0''), by software modules called ``Device Drivers''. In order to access a custom hardware device from the application level, a programmer must do the following:

  1. Learn the internals of the operating system he is working on (95/98/ME/NT / CE / Linux / Solaris... )
  2. Learn how to write a device driver.
  3. Learn new tools for developing/debugging in the Kernel Mode (DDK, ETK, DDI/DKI )
  4. Write the Kernel Mode device driver that does the basic hardware input / output.
  5. Write the application in the User Mode, which accesses the hardware through the device driver written in the Kernel Mode.
  6. Repeat the first four steps for each new operating system on which the code should run.

1.3  WinDriver Advantages

Easy development - WinDriver enables Windows programmers to create PCI/PCMCIA/ISA/ISA PnP/EISA/CompactPCI and USB based device drivers in an extremely short time. WinDriver allows you to create your driver in the ``User Mode'' in the familiar environment - using MSDEV, Visual C/C++, Borland Delphi, Borland C++, Visual Basic, GCC or any other 32-bit compiler. WinDriver eliminates the need for you to be familiar with the operating system internals, kernel programming or with the DDK,ETK,DDI/DKI or have any device driver knowledge.

Cross Platform - The driver created with WinDriver will run on Windows 95/ 98/ME/NT/2000/CE, Linux, Solaris, VxWorks and OS/2, - i.e. write once - run on any of these platforms.

Friendly Wizards - DriverWizard (included) is a graphical diagnostics tool that lets you write to, and read from the hardware, before writing a single line of code. With a few clicks of the mouse, the hardware is diagnosed - memory ranges are read, registers are toggled and interrupts are checked. Once the device is operating to your satisfaction, DriverWizard creates the skeletal driver source code, giving access functions to all the resources on the hardware.

Kernel Mode Performance - WinDriver's API is optimized for performance. For drivers that need kernel mode performance, WinDriver offers the ``Kernel PlugIn''. This powerful feature enables you to create and debug your code in the user mode, and run the performance critical parts of your code, (such as the interrupt handler, or access to I/O mapped memory ranges), in kernel mode, thereby achieving kernel mode performance (zero performance degradation).

This unique feature allows the developer to run the user mode code in the OS kernel without having to learn how the kernel works. When working with Windows CE or VxWorks, there is no need to use the Kernel PlugIn since Windows CE and VxWorks have no separation between user mode and kernel mode. This enables you to achieve optimal performance from the user mode code.

How fast can WinDriver go?

Using the WinDriver Kernel PlugIn you can expect the same throughput of a custom Kernel Driver. You are confined only by your operating system and hardware limitations. A ballpark figure of the throughput you can reach using the Kernel PlugIn would be about 100,000 interrupts per second.

User Mode ease - Kernel Mode performance!

To conclude - using WinDriver, all a developer has to do to create an application that accesses the custom hardware is:

  1. Start DriverWizard and detect the hardware and its resources.

  2. Automatically generate the device driver code from within DriverWizard.

  3. Call the generated functions from the User Mode application.

The new hardware access application now runs on all Windows platforms (including CE), on Linux, on Solaris,on VxWorks and on OS/2 (just recompile).

1.4  WinDriver Feature List

Notes

  1. PCMCIA is only supported in the Windows CE version.

1.5  WinDriver Architecture

images/common12.png

Figure 1.1: WinDriver Architecture

For hardware access, your application calls one of the WinDriver functions from the WinDriver User Mode library (windrvr.h). The User Mode library calls the WinDriver Kernel, which accesses the hardware for you, through the native calls of the operating system.

WinDriver's design minimizes performance hits on your code, even though it is running in the User Mode. However, some hardware drivers need performance, which cannot be achieved from the User Mode. This is where WinDriver's edge sharpens - after easily creating and debugging your code in the User Mode, you may `drop' the performance critical modules of your code (such as a hardware interrupt handler) into the WinDriver Kernel PlugIn without changing a single line of it. Now, WinDriver Kernel calls this module from the Kernel Mode, thereby achieving maximal performance. This allows you to program and debug in the User Mode, and still achieve kernel performance where needed. In Windows CE and VxWorks there is no separation between User Mode and Kernel Mode, therefore you may achieve optimal performance directly from the user mode, eliminating the need to use the Kernel PlugIn in this OS.

1.6  What Platforms does WinDriver Support?

WinDriver supports Windows 95/98/ME/NT/2000/CE, Solaris, VxWorks, OS/2 and Linux. The same source code will run on all supported platforms. The same executable you write will operate on Windows 95, 98, ME, NT and 2000. Even if your code is meant only for one of these operating systems, using WinDriver will give you the flexibility of moving your driver to the other operating system without changing your code.

1.7  Can I Try WinDriver Before I Buy?

Yes! ¡ Evaluation versions of WinDriver for all supported operating systems and buses are available at the Jungo web site at http://www.jungo.com/dnload.html

1.8  Limitations of the different evaluation versions

All the evaluation versions of WinDriver are full featured. No functions are limited or crippled in any way. The following is a list of the differences between the evaluation versions and the registered ones:

1.9  How do I develop my Driver with WinDriver? (Overview)

1.9.1  On Windows 95, 98, ME, NT and 2000

  1. Start DriverWizard (See the Chapter detailing DriverWizard 4 for details). Diagnose your card, and let DriverWizard generate skeletal code for you. The code generated by DriverWizard is a diagnostics program, containing functions that read and write to any resource detected or defined (including custom defined registers), and enables your card's interrupts and listens to them. Modify the code generated by DriverWizard, to suit your particular application needs.
  2. Run and debug your driver in the User Mode.
  3. If your code contains performance critical sections, improve their performance by referring to Chapter 11 . This Chapter provides a checklist of tune-ups you can make in your code, and shows you how to take the performance critical sections and move them to the ''Kernel PlugIn''.

1.9.2  On Windows CE

  1. Plug your hardware into your NT machine.
  2. Install the CE ETK on NT.
  3. Diagnose your hardware via DriverWizard and then let it generate your driver's skeletal code. Modify this code using Visual C++ to meet your specific needs. Test and debug your code and hardware from the CE emulation running on the NT machine.
  4. If you cannot plug your hardware into your NT machine, you may still use DriverWizard by manually entering all your resources into it. Let DriverWizard generate your code and then test it on your hardware using ser al connection. After verifying that the generated code works properly, modify it to meet your specific needs. You may also use (or combine) any of the sample files for your driver's skeletal code.
  5. If your code contains performance critical sections, improve their performance by referring to the Chapter 11.

1.9.3  On Linux and Solaris

From version 5.0 and onwards,WinDriver offers a GUI DriverWizard that facilitates Driver Development on Linux and Solaris. Use the GUI DriverWizard for Linux and Solaris in the same way as you use the one on Windows and then generate Linux and Solaris code.

If you are using WinDriver 4.x and below, and you do not use the Linux or Solaris X11 GUI,you may consider Windows as your initial development platform. It is recommended to start the development process on your Windows machine, using DriverWizard in the same way as described above.

If you do not have a Windows machine, you may use the sample files included with WinDriver as skeletons for your driver and change them using the WinDriver API.

1.9.4  On Embedded Operating Systems

For embedded operating systems, like Windows CE or VxWorks, you can use the new Remote WinDriver feature. Just run DriverWizard on a supported Host platform and you can detect and diagnose your hardware on the remote embedded target using the new Remote WinDriver option.

Note: Cross-endian network communication support is not yet provided and therefore both the host and the target machine must have the same endian scheme when using Remote WinDriver. For example, when detecting and diagnosing hardware on a Motorola PPC target architecture, you can use a Sparc machine as a compatible host since both Motorola and Sparc use the same endian scheme.

1.10  What Does the WinDriver Toolkit Include?

The following modules are included in your WinDriver toolkit:

1.10.1  WinDriver Modules

1.10.2  Utilities

The CE version includes:

1.10.3  WinDriver's SPECIFIC CHIP-SET SUPPORT

These are APIs that support the major PCI bridge chip-sets, for even faster code development.

Each of these directories includes the following subdirectories:

1.10.4  Samples

Here you will find the source code for the utilities listed above, along with other samples which show how various driver tasks are performed. Find the sample which is closest to the driver you need. Use it to jump-start your driver development process.

1.11  Can I distribute the driver created with WinDriver?

Yes. WinDriver is purchased as a development toolkit, and any device driver created using WinDriver may be distributed royalty free in as many copies as you wish. See the license agreement (\windriver\docs\license.txt) for more details.

1.12  Device Driver Overview

The following is an overview of the common types of device driver architectures:

1.12.1  Monolithic Drivers

These are the device drivers that are primarily used to drive custom hardware. A monolithic driver is accessed by one or more user applications, and directly drives a hardware device. The driver communicates with the application through I / O control commands - (IOCTLs), and drives the hardware through calling the different DDK, ETK, DDI/DKI functions.

images/Monolithic.png

Figure 1.2: Monolithic Drivers

Monolithic drivers are encountered under all operating systems including all Windows platforms (95/98/ME, NT/2000, CE), all Unix platforms (Linux, VxWorks and Solaris), and others like OS/2.

1.12.2  Windows 95/98/ME Drivers

We use the term Windows drivers to mean VxD drivers that run on the related family of OS's, Windows 95, Windows 98 and Windows ME. These drivers do not work on Windows NT. Windows drivers are typically monolithic in nature. They provide direct access to hardware and privileged operating system functions. These drivers are called VxD drivers. Windows drivers can be stacked or layered in any fashion. However, the driver structure itself does not impose any layering.

1.12.3  NT Driver Model

Besides monolithic drivers, Windows NT defines other kinds of drivers that are generally unique to Windows NT, but subsets or minor variations of which, are supported on other Windows operating systems like Windows 95/98/ME and WinCE. These are discussed below.

1.12.3.1  Layered Drivers

Layered drivers are device drivers that are part of a ``stack'' of device drivers, that together process an I/O request. An example of a layered driver is a driver that intercepts calls to the disk, and encrypts / decrypts all data being written / read from the disk. In this example, a driver would be hooked on to the top of the existing driver and would only do the encryption / decryption.

Layered drivers are sometimes also known as filter drivers. These are also supported on Windows 95/98/ME.

images/Layered.png

Figure 1.3: Layered Drivers

1.12.3.2  Miniport Drivers

There are classes of device drivers in which much of the code has to do with the functionality of the device, and not with the device's inner workings.

images/Miniport.png

Figure 1.4: Miniport Drivers

Windows NT/2000, for instance, provides several driver classes (called ``ports'') that handle the common functionality of their class. It is then up to the user to add only the functionality that has to do with the inner workings of the specific hardware.

An example of Miniport drivers is the ``NDIS'' miniport driver. The NDIS miniport framework is used to create network drivers that hook up to NT's communication stacks, and are therefore accessible by the common communication calls from within applications. The Windows NT kernel provides drivers for the different communication stacks, and other code that is common to communication cards. Due to the NDIS framework, the network card developer does not have to write all of this code, the developer must only write the code that is specific to the network card that he is developing.

1.12.4  Unix Device Drivers

In the classic Unix driver model, devices belong to three categories, character (char) devices, block devices and network devices. Drivers that implement these devices are correspondingly known as char drivers, block drivers or network drivers. Under Unix, drivers are code units that are linked into the kernel, and run in privileged kernel mode. Generally, driver code runs on behalf of the user mode application. Access to Unix drivers from user mode applications is provided via the filesystem. In other words, devices appear to the applications as special device files that can be opened.

The three classes of devices are:

1.12.5  Linux Device Drivers

Linux device drivers are based on the classic Unix device driver model. In addition, Linux introduces some of its own characteristics.

Under Linux, block devices can also be accessed like a character device, but has an additional block oriented interface which is invisible to the user or application.

Traditionally, under Unix, device drivers had to be linked with the kernel, and the system had to be brought down and restarted after installing a new driver. Linux introduced the concept of a dynamically loadable driver called a module. Linux modules can be loaded or removed dynamically without requiring the system to be shut down. All Linux drivers can be written so that they are statically linked, or in modular form, which makes them dynamically loadable. This makes Linux memory usage very efficient because modules can be written to probe for their own hardware and unload themselves if they cannot find the hardware they are looking for.

1.12.6  Solaris Device Drivers

Solaris device drivers are also based on the classic Unix device driver model. Like Linux, Solaris drivers may either be statically linked with the kernel, or may be dynamically loaded and removed, from the kernel.

1.13  Matching the right tool for your driver

Jungo offers two driver development products lines: WinDriver and KernelDriver. WinDriver is a tool designed for monolithic type user mode drivers. WinDriver enables you to access your hardware directly from within your Win32 application, without writing a kernel mode device driver. Using WinDriver you can either access your hardware directly from your application (in user mode) or write a DLL that you can call from many different applications.

WinDriver also provides a complete solution for high performance drivers. Using WinDriver's Kernel PlugIn, you can `drop' your user mode code into the kernel and reach full kernel mode performance.

A driver created with WinDriver runs on Windows 95, 98, ME, NT, 2000, CE, Linux, Solaris, VxWorks and OS/2. Typically, a developer without any previous driver knowledge can get a driver running in a matter of a few hours (compared to several weeks with a kernel mode driver).

There are situations that require drivers to be running in the kernel mode. Network drivers under Linux and Windows for example, almost always need to reside in the kernel. In addition under Windows NT, for layered or miniport drivers, kernel programming is necessary. To simplify this difficult task, Jungo provides ``KernelDriver'' - a tool kit for writing kernel mode drivers for Windows platforms (95/98/ME/NT/2000) and Linux. In addition, KernelDriver has special support for NT/2000 - a C++ toolkit that provides classes that encapsulate thousands of lines of kernel code, enabling you to focus on your driver's added-value functionality, instead of your OS internals.

Chapter 2
WinDriver USB Overview

This chapter explores the basic characteristics of the USB bus and introduces WinDriver USB features and architecture.

2.1  Introduction to USB

USB, short for Universal Serial Bus, is a new industry-standard extension to the PC architecture, for attaching peripherals to the computer. The Universal Serial Bus was originally developed in 1995 by leading PC and telecommunication industry companies, such as Intel, Compaq, Microsoft and NEC. The motivation for the development of USB, was fueled because of several considerations. Among them are the needs for an inexpensive and widespread connectivity solution for peripherals in general and for the ``Computer Telephony Integration'' in particular, the need for an easy to use and flexible method of reconfiguring the PC and a solution for adding a large number of external peripherals.

The USB interface meets the needs stated above. A single USB port can be used to connect up to 127 peripheral devices. USB also supports Plug-and-Play installation and hot swapping. USB 1.1 supports both isochronous and asynchronous data transfers and has dual-speed data transfer; 1.5Mbps (Megabit per second) for low-speed USB devices and 12Mbps for high-speed USB devices (much faster than the original serial port). Cables connecting the device to the PC can be up to five meters (16.4 feet) long. USB includes built-in power distribution for low power devices, and can provide limited power (maximum: 500mA of current) to devices attached on the bus.

Because of these benefits, USB is enjoying broad market acceptance today.

The next generation (USB2.0) supports a faster signalling rate of 480 Mb/S that is 40 times faster than USB 1.1. USB2.0 is fully forward and backward compatible with USB1.1 and uses the existing cables and connectors.

USB2.0 supports a connection for higher bandwidth, higher functionality PC peripherals. In addition, it has the capability to handle more simultaneously running peripherals.

USB2.0 will benefit many applications like Interactive Gaming, Broadband Internet Access, Desktop and Web Publishing, Internet Services and Conferencing.

2.2  Feature List

2.3  USB Components

USB Host: The USB host computer is where the USB host controller is installed, and where the client software / device driver runs. The USB host controller is the interface between the host and the USB peripherals. The host is responsible for detecting attachment and removals of USB devices, managing the control and data flow between the host and the devices, providing power to attached devices and more.

USB Hub: A USB device that enables connecting additional USB devices to a single USB port on the USB host. Hubs on the back plane of the hosts are called root hubs. Other hubs are external hubs.

USB Function: The USB device that is able to transmit or receive data or control information over the bus, and provides a function. Compound devices provide multiple functions on the USB bus.

2.4  Data Flow in USB Devices

During the operation of the USB device, data flows between the client software and the device. The data is moved between memory buffers of the software on the host and the device, using pipes, which end in endpoints on the device side.

An endpoint is a uniquely identifiable entity on the USB device, which is the source or the terminus of the data that flows from or to the device. Each USB device, logical or physical, has a collection of independent endpoints. Endpoint attributes are their bus access frequency, their bandwidth requirement, their endpoint number, their error handling mechanism, the maximum packet size that the endpoint can transmit or receive, their transfer type and their direction (into the device / out of the device).

Pipes are logical components, representing associations between an endpoint on the USB device and software on the host. The data is moved to and from the device `through' a pipe. A pipe can be of two modes: stream pipe and message pipe, according to the type of data transfer used in that pipe. Pipes, sending data in interrupt, bulk or isochronous types are stream pipes, while control transfer type is supported by the message pipes. The different USB transfer types are discussed below:

images/USBENDPOINT.png

Figure 2.1: USB Endpoints

2.5  USB Data Exchange

The USB standard supports two kinds of data exchange between the host and the device: functional data exchange and control exchange.

More information on how to implement the control transfer by sending Setup Packets can be found in Chapter 10 that deals with WinDriver Implementation Issues.

The screen shot below shows a USB device with one bi-directional control and three functional data transfer pipes/endpoints:

images/usbpipe.png

Figure 2.2: USB Pipes

2.6  USB Data Transfer Types

The USB device (function) communicates with the host by transferring data through a pipe between a memory buffer on the host and an endpoint on the device. USB provides different transfer types, that best suit the service required by the device and by the software. The transfer type of a specific endpoint is determined in the endpoint descriptor.

There are four different types of data transfer within the USB specification:

Control Transfer: Control transfer is mainly intended to support configuration, command and status operations between the software on the host and the device. Each USB device has at least one control pipe (default pipe), which provides access to the configuration, status and control information. The control pipe is a bi-directional pipe. Control transfer is bursty, non-periodic communication. Control transfer has a robust error detection, recovery and retransmission mechanism and retries are made with no involvement of the driver. Control transfer is used by low speed and high- speed devices.

Isochronous Transfer: A type usually used for time dependent information, such as multimedia streams and telephony. The transfer is periodic and continuous. The isochronous pipe is uni-directional and a certain endpoint can either transmit or receive information. For bi-directional isochronous communication there's a need to use two isochronous pipes, one in each direction. USB guarantees the isochronous transfer access to the USB bandwidth (that is it reserves the required amount of bytes of the USB frame) with bounded latency and guarantees the data transfer rate through the pipe unless there is less data transmitted. Up to 90% of the USB frame can be allocated to periodic transfers (isochronous and interrupt transfers). If, during configuration, there is no sufficient bus time available for the requester isochronous pipe, the configuration is not established. Since time is more important than correctness in these types of transfers, no retries are made in case of error in the data transfer, though the data receiver can determine the error that occurred on the bus. Isochronous transfer can be used only by high-speed devices.

Interrupt Transfer: Interrupt transfer is intended for devices that send and receive small amounts of data, in low frequency or in an asynchronous time frame. An interrupt transfer type guarantees a maximum service period and a retry of delivery to be attempted in the next period, in case of an error on the bus. The interrupt pipe, like the isochronous pipe, is uni-directional. The bus access time period (1-255ms for high-speed devices and 10-255ms for low-speed devices) is specified by the endpoint of the interrupt pipe. Although the host and the device can count only on the time period indicated by the endpoint, the system can provide a shorter period up to 1 ms.

Bulk Transfer: Bulk transfer is non-periodic, large packet, bursty communication. Bulk transfer typically supports devices that transfer large amounts of non-time sensitive data, and that can use any available bandwidth, such as printers and scanners. Bulk transfer allows access to the bus on availability basis, guarantees the data transfer but not the latency and provides error-check mechanism with retries attempts. If part of the USB bandwidth is not being used for other transfers, the system will use it for bulk transfer. Like previous stream pipes (isochronous and interrupt) the bulk pipe is also uni-directional. Bulk transfer can only be used by high-speed devices.

2.7  USB Configuration

Before the USB function (or functions in a compound device) can be operated, the device must be configured. The host does the configuring, by acquiring the configuration information from the USB device. USB devices report their attributes by descriptors. A descriptor is the defined structure and format in which the data is transferred. A complete description of the USB descriptors can be found in Chapter 9 of the USB Specification (See http://www.usb.org for the full specification).

It is best to view the USB descriptors as a hierarchic structure of four levels:

There is only one device descriptor for each USB device. Each device has one or more configurations, that have one or more interfaces, and each interface has zero or more endpoints.

Device Level: At the top level is the `device descriptor', that includes general information about the USB device, that is global information for all of the device configurations. The device descriptor describes, among other things, the device class (USB devices are divided into device classes, such as HID devices, hubs, locator devices etc.), subclass, protocol code, Vendor ID, Device ID and more. Each USB device has one device descriptor.

Configuration Level: A USB device has one or more configuration descriptors, which describe the number of interfaces grouped in each configuration and power attributes of the configuration (such as self-powered, remote wakeup, maximum power consumption and more). At a given time, only one configuration is loaded. An example of different configurations of the same device may be an ISDN adapter, where one configuration presents it with a single interface of 128KB/s and a second configuration with two interfaces of 64KB/s.

Interface Level: The interface is a related set of endpoints that present a specific functionality or feature of the device. Each interface may operate independently. The interface descriptor describes the number of the interface, number of endpoints used by this interface, and the interface specific class, subclass and protocol values when the interface operates independently. In addition, an interface may have alternate settings. The alternate settings allow the endpoints or their characteristics to be varied after the device is configured.

Endpoint Level: The lowest level is the endpoint descriptor that provides the host with information regarding the data transfer type of the endpoint and the bandwidth of each endpoint (the maximum packet size of the specific endpoint). For isochronous endpoints, this value is used to reserve the bus time required for the data transfer. Other attributes of the endpoints are their bus access frequency, their endpoint number, their error handling mechanism, and their direction.

Seems complicated? Not at all! WinDriver automates the USB configuration process. The included DriverWizard and USB diagnostics application, scan the USB bus, detect all USB devices and their different configurations, interfaces, settings and endpoints, and enables the developer to pick the desired configuration before starting driver development.

WinDriver identifies the endpoint transfer type as determined in the endpoint descriptor. The driver created with WinDriver contains all configuration information acquired at this early stage.

2.8  WinDriver USB

WinDriver USB enables developers to quickly develop high performance drivers for USB based devices, without having to learn the USB specifications or the OS internals. Using WinDriver USB, developers can create USB drivers without having to use the DDK (Microsoft Driver Development Kit), and without having to be familiar with Microsoft's WDM (Win32 Driver Module).

The driver code developed with WinDriver USB is binary compatible between Windows 2000, Windows ME and Windows 98.

The source code will be code-compatible among all other operating systems, supported by WinDriver USB. For up to date information regarding operating systems currently supported by WinDriver USB, please check Jungo's web site at
http://www.jungo.com

WinDriver USB encapsulates the USB specification and architecture, letting you focus on your application logic. WinDriver USB features DriverWizard, with which you can detect your hardware, configure it and test it before writing a single line of code. DriverWizard will lead you through the configuration procedure first, enable you to choose the desirable configuration, interface and alternate setting through a friendly graphical user interface. After detecting and configuring your USB device, you can then test it, listen to pipes, write and read packets and ensure that all your hardware resources function as expected. WinDriver USB is a generic tool kit, which supports all USB devices, from all vendors and with all types of configurations.

After your hardware is diagnosed, DriverWizard automatically generates your device driver source code in C or in Delphi. WinDriver USB provides user-mode APIs to your hardware, which you can call from within your application. The WinDriver USB API is specific for your USB device and includes USB unique operations such as reset-pipe and reset-device. Along with the device API, WinDriver USB creates a diagnostics application, which just needs to be compiled and run. You can use this application as your skeletal driver to jump-start your development cycle. If you are a VB programmer, you will find all WinDriver USB API supported for you also in VB, giving you everything you need to develop your driver in VB.

DriverWizard also automates the creation of a .INF file where needed. The .INF file is a text file used by the Plug-&-Play mechanisms of Windows 95/98/ME and Windows 2000 to load the driver for the newly installed hardware or to replace an existing driver. The .INF file includes all necessary information about the device(s) and the files to be installed. .INF files are required for hardware that identify themselves, such as USB and PCI. In some cases, the .INF file of your specific device is included in the .INF files that are shipped with the operating system. In other cases, you will need to create a .INF file for your device. WinDriver automates this process for you. More information on how to create your own .INF file with DriverWizard can be found in Chapter 4 that explains the DriverWizard. Installation instructions of .INF files can be found in Chapter 21 that illustrates how to distribute your driver.

Using WinDriver USB, all development is done in the user mode, using familiar development and debugging tools and your favorite compiler (such as MSDEV, Visual C/C++, Borland Delphi, Borland C++, Visual Basic).

WinDriver USB API is designed to give you optimized performance. In cases where native kernel mode performance is needed, use WinDriver USB's unique `KernelPlugIn' feature (included). This powerful feature enables you to write and debug your code in the user mode, and then simply `drop' it into the Kernel PlugIn for kernel mode execution. This unique architecture enables you to achieve maximum performance with user mode ease of use.

All other WinDriver USB features can be found in the WinDriver feature list in Chapter 2 that covers the USB features of WinDriver.

2.9  WinDriver USB Architecture

images/WDUSBARCH.png

Figure 2.3: WinDriver USB Architecture

NOTE: Any occurrence of wdusb in the above figure should be read as wdpnp. As of version 5.0 the file wdusb.sys has been changed to wdpnp.sys and supports PCI, PCMCIA and USB devices.

To access your hardware, your application calls the required WinDriver USB API function from the WinDriver User Mode Library (windrvr.h). The User Mode Library calls the WinDriver Kernel Module. The WinDriver Kernel Module is comprised of windrvr.sys and wdpnp.sys. The WinDriver Kernel Module accesses your USB device resources through the native operating system calls.

There are two layers responsible to abstract the USB device to the USB device driver:

The upper one is the USB Driver layer (including the USB Driver (USBD) and USB Hub Driver) and the lower one is the host controller driver layer (HCD). The division of duties between the HCD and USBD is not defined, and is operating system dependent. Both HCD and USBD are software interfaces and components of the operating system, where the HCD layer represents a lower level of abstraction.

The HCD is the software layer that provides an abstraction of the host controller hardware while the USBD provides an abstraction of the USB device and the data transfer between the host software and the function of the USB device.

The USBD communicates with its clients (the specific device driver for example) through the USB Driver Interface ¡ USBDI. At the lower level, the USBD and USB Hub Driver implement the hardware access and data transfer by communicating with the HCD using the Host Controller Driver Interface HCDI.

The USB Hub Driver is responsible for identifying addition and removal of devices from a particular hub. Once the Hub Driver receives a signal that a device was attached or detached, it uses additional host software and the USBD to recognize and configure the device. The software implementing the configuration can include the hub driver, the device driver and other software.

WinDriver USB abstracts the configuration procedure and hardware access described above for the developer. With WinDriver USB API, developers can do all the hardware-related operations without having to master the lower levels of implementing these activities.

2.10  What drivers can I write with WinDriver USB?

Almost all monolithic drivers (drivers that need to access specific USB devices), can be written with WinDriver USB. In cases where a ``standard'' driver needs to be written (e.g. NDIS driver, SCSI driver, Display driver, USB to Serial port drivers, USB layered drivers, etc.)., use KernelDriver USB (also from Jungo).

For quicker development time, select WinDriver USB over KernelDriver USB wherever possible.

Chapter 3
Installation and Setup

This chapter takes you through the WinDriver installation process, and shows you how to check that your WinDriver is properly installed. The last section discusses the uninstallation procedure.

3.1  Systems Requirements

3.1.1  For Windows 95 / 98 / ME

  1. An x86 processor

  2. Any 32-bit development environment supporting C, VB or Delphi.

3.1.2  For Windows NT/2000

  1. An x86 processor.

  2. Any 32-bit development environment supporting C, VB or Delphi.

3.1.3  For Windows CE

  1. Windows NT Workstation 4.0 host development platform

  2. Microsoft Developer Studio 97 including:

If you are using a commercial Windows CE hand-held Computer like the HP Jornada or the Sharp Mobilon, you will need the following items in addition to the ones mentioned above:

If you are using an X86 PC or a commercial target board like the Hitachi ODO, you will need the following items in addition:

This procedure is explained in detail in the online documentation of the Windows CE ETK and Platform Builder.

3.1.4  For Linux

  1. An x86 processor

  2. Any 32-bit development environment supporting C (such as GCC).

3.1.5  For Solaris

  1. An x86 processor

  2. Any 32-bit development environment supporting C (such as GCC).

3.1.6  For VxWorks

  1. Windows NT Workstation 4.0 host platform development

  2. Tornado II IDE

  3. Selected Target Platform: This should be running a processor that has a BSP (Board Support Package) compatible with the list of CPU/BSP combinations supported by DriverBuilder.

    For an up-to-date list, see the URL below:
    http://www.jungo.com/db-vxworks.html#platforms

    For information on BSP compatibility, please contact your nearest Wind River Systems support representative.

3.2  Installing WinDriver

The WinDriver CD contains all versions of WinDriver for all the different operating systems. The CD's root directory contains the Windows 95/98/ME and NT/2000 version. This will automatically begin when you insert the CD into your CD drive. The other versions of WinDriver are located in subdirectories i.e. \ Linux, \Wince and so on.

3.2.1  Installing WinDriver for Windows 95,98,ME,NT and 2000

  1. Insert the WinDriver CD into your CD-ROM drive. (When installing WinDriver by downloading it from Jungo's web site instead of using the WinDriver CD, double click the downloaded WinDriver file (WDxxx.exe) in your download directory, and go to step 3).

  2. Wait a few seconds until the installation program starts automatically. If for some reason it does not start automatically, double-click the file ``Wdxxx.exe'' (where ``xxx'' is the version number) and click the ``Install WinDriver'' button.

  3. Read the license text carefully, and click `YES' if you accept its terms.

  4. The evaluation package installer proceeds to copy all the files and may prompt you to reboot your system.

    The following steps are For Registered Users only:

  5. Choose `Install registered version' when prompted for the version to install.

  6. In the ``Setup type'' screen, choose one of the following:

  7. You will now be prompted for an 8-digit password to continue the installation. Type in the password you received when purchasing WinDriver. Take care when entering the password. The installation will fail if the wrong password is written here. Note that the password is case sensitive.

  8. After completing the set-up, please reboot your computer.

  9. Activate DriverWizard from Start | Programs | WinDriver | DriverWizard. Select the Register WinDriver option from the File menu and insert your license string there.

  10. To activate source code you have developed in the evaluation version, simply follow the instructions in windriver\redist
    \register\register.txt
    .

3.2.2  Installing WinDriver CE

3.2.2.1  Installing WinDriver CE for a hand held PC

Insert the WinDriver CD into your NT machine CD drive.

Exit from the auto installation and double click the ``Cd_setup.exe'' file from the \Wince directory inside the CD. This will copy all needed WinDriver files to your development platform (NT).

Copy the WinDriver CE kernel file
\windriver\redist\register\TARGET_CPU\windrvr.dll)
to the \WINDOWS subdirectory of your HPC.

Use the Windows CE Remote Registry Editor tool(ceregedt.exe) or the Pocket Registry Editor(pregedt.exe) on your HPC to modify your registry so that the WinDriver CE kernel is loaded appropriately. The file
\windriver\samples\wince_install\PROJECT_WD.REG
contains the appropriate changes to be made.

Restart your HPC. The WinDriver CE kernel will be automatically loaded. You will have to do a warm RESET rather than just Suspend/Resume. You should look for a button labelled RESET on your HPC. On the HP 3xx/6xx series, this button can be found under the reserve battery cover.

Compile and run the sample programs (see Section 3.4 that describes how to check your installation) to make sure that WinDriver CE is loaded and is functioning correctly.

3.2.2.2  Using WinDriver CE with ETK/Platform Builder

3.2.3  Testing Your Applications on the X86 HPC Emulator

3.2.3.1  Emulation on Windows NT

  1. Repeat step 1-2 in the first CE installation set of instructions.

  2. Select the X86EMU target and then compile and run one of the sample programs to make sure that it works correctly.

3.2.4  Installing WinDriver for Linux

Since WinDriver installation installs the kernel module windrvr.o, WinDriver should be installed by the system administrator logged in as root, or with root privileges.

The following steps are for Registered Users only

  1. Change directory to /windriver/redist/register/ (/usr/local/WinDriver> cd redist/register)

  2. Extract the file wdxxxreg.zip using the password you have received with the WinDriver package (/usr/local/WinDriver/redist/register> ../../util/unzip WDxxxREG.ZIP)

  3. Change directory to windriver/redist/ (/usr/local/WinDriver/redist/register/>cd ..)

  4. Remove the evaluation module if previously installed ( /usr/local/WinDriver/redist/> /sbin/rmmod windrvr)

  5. Clean the evaluation version module directory ( /usr/local/WinDriver/redist/> make clean)

  6. Install registered version ( /usr/local/WinDriver/redist/> make install IS_REGISTERED=1)

    Once you activate DriverWizard you will prompted for the license string you have received when purchasing the registered version.

  7. To activate source code you have developed in the evaluation version, simply follow the instructions in WinDriver/redist/register/register.txt.

3.2.4.1  Restricting hardware access on Linux

CAUTION: Since /dev/windrvr gives direct hardware access to user programs, it may compromise kernel stability on multi-user Linux systems. Please restrict access to DriverWizard and the device file /dev/windrvr to trusted users.

For security reasons the Windriver installation script does not automatically perform the steps of changing the permissions on /dev/windrvr and the DriverWizard executable (wdwizard).

3.2.5  Installing DriverWizard on your Windows machine

  1. Insert the WinDriver CD into your Windows machine CD drive.

  2. Follow steps 2-9 of the Windows installation instructions (above).

3.2.6  Installing WinDriver for Solaris

Since WinDriver installation installs the kernel module windrvr.o, it should be installed by the system administrator logged in as root, or with root privileges.

  1. Insert your CD into your Solaris machine CD drive or copy the downloaded file to your preferred directory (eg. /home/username/).

  2. Change directory to preferred installation root directory, say /usr/local: ( cd /usr/local/)

  3. Copy the file WDxxxSLS.tgz (Sparc) or WDxxxSL.tgz (Intel) to the current directory (here xxx stands for the version number, for example 500): (/usr/local/> cp /home/username /WDxxxSL.tgz ./)

    Note: When installing WinDriver for Solaris x86 use WDxxxSL.tgz instead of WDxxxSLS.tgz.

  4. Extract the file (/usr/local/> gunzip -c WDxxxSLS.tgz | tar -xvf WDxxxSLS.tar)

  5. Change directory to WinDriver (Note: In V5.0 this directory gets created by tar but in versions preceding 5.0, the WinDriver directory does not get created by the extraction. Therefore with older versions like 4.3, first create a directory (say WinDriver) before proceeding with the installation.)

  6. Install WinDriver for Solaris (/usr/local/WinDriver>./ install_windrvr)

  7. Create a symbolic link so that you can easily launch the GUI DriverWizard (/usr/local/WinDriver>ln -s /usr/local/WinDriver/wizard/wdwizard /usr/bin/wdwizard).

  8. Change the read and execute permissions on the file wdwizard so that ordinary users can access this program

  9. Change the user and group ids and give read/write permissions to the device file /dev/windrvr depending on how you wish to allow users to access hardware through the device.

The following steps are for Registered Users only:

3.2.6.1  Restricting hardware access on Solaris

CAUTION: Since /dev/windrvr gives direct hardware access to user programs, it may compromise kernel stability on multi-user Solaris systems. Please restrict to trusted users, access to DriverWizard and the device file /dev/windrvr.

For security reasons the Windriver installation script does not automatically perform the steps of changing the permissions on /dev/windrvr and the DriverWizard executable (wdwizard).

3.2.6.2  Solaris Platform Specific Issues

WinDriver for Solaris supports version 2.6, 7.0 and 8.0 on Intel X86 and Sparc. The same WinDriver based hardware access code will run on both platforms after recompilation.

WinDriver does not support Solaris 7 64 bit kernel. To switch from a 64 bit kernel to a 32 bit kernel follow these simple steps:

  1. Reboot the computer (as super user)-#reboot

  2. When the computer resets, Break into the boot prompt by pressing STOP+A.

  3. At the prompt enter the following: (boot kernel/unix)

  4. To make the 32 bit kernel to be the default one, enter the following at the boot prompt: (setenv boot-file kernel/unix)

3.2.7  Installing DriverBuilder for VxWorks

The following describes the installation of DriverBuilder for VxWorks. DriverBuilder development environment works with Tornado 2 for Windows only (on x86 platform). Drivers generated using version 5.0 of DriverBuilder will run on Intel x86 BSPs (pc486, pcPentium and pcPentiumPro), PPC 821/860 with MBX821/860 and PPC 750 (IBM PPC 604) with MCP750. For an up-to-date list, see the URL below:
http://www.jungo.com/db-vxworks.html#platforms

To install DriverBuilder:

  1. Download DriverBuilder for VxWorks

  2. Change drive to the preferred root drive for DriverBuilder, for example "c:\"

  3. Unpack the file you downloaded - for example: ünzip -d DB500VX.zip c:\". The extraction creates a directory called DriverBuilder under which all the DriverBuilder installation files can be found.(this feature was added in version 5.00. If you are working on a previous version,please create a directory for DriverBuilder,for example: "c:\cd_vxworks" and unpack the file to it: ünzip -d DBxxxVX.zip c:\db_vxworks").

  4. Create a shortcut on your desktop to DriverWizard found under C:\DriverBuilder\wizard\wdwizard.exe so that you can easily launch the GUI DriverWizard

Note: The WinDriver samples for VxWorks have the .out extension. For example, pci_diag.out. To invoke these programs, use Windsh to load them, and execute the routine xxx_main. For example:

wddebug.out : wddebug_main

pci_diag.out : pci_diag_main

3.3  Upgrading Your Installation

To upgrade to a new version of WinDriver, follow the steps outlined in Section 3.2.1 that illustrates the process of installing WinDriver for Windows 95/98/ME/NT/2000. You can either choose to overwrite the existing installation or install to a separate directory.

After installation, start DriverWizard and enter the new license string, if you have received one. This completes the upgrade of WinDriver.

To upgrade your source code, navigate to the RegisterWinDriver() function call in your source code, and pass the new license string as a parameter to this function. For more information on RegisterWinDriver(), please refer to the file register.txt in the directory WinDriver\redist\register.

The procedure for upgrading your installation on other operating systems is the same as the one described above. Please check the respective installation sections for installation details.

3.4  Checking Your Installation

3.4.1  On your Windows machine

Start DriverWizard by choosing `Programs |WinDriver | DriverWizard' from the Start menu.

Registered Users

3.4.2  On your Windows CE machine

3.4.3  On your Linux machine

3.4.4  On your Solaris machine

3.4.5  On VxWorks

  1. In x86 only: Make sure MMU is set to basic support ("hardware/memory/MMU/MMU Mode"). DriverBuilder for ``no MMU'' support will be available shortly.

  2. Load DriverBuilder: download the object file

    (DriverBuilder \redist\eval\intelx86\PENTIUM\windrvr.o).

  3. Initialize DriverBuilder: from the WindShell:

           => drvrInit()
    
           function returned (return value = 0)
           
            =>
    

  4. Run a sample driver: load

    C:\DriverBuilder \samples\pci_diag\PENTIUM\pci_diag.out

    from the WindShell:

           => pci_diag_main()
    

    now you can scan the PCI bus, open cards and `talk' to them.

3.5  Uninstalling WinDriver

If for some reason you wish to uninstall either the evaluation or registered version of WinDriver, please refer to this section.

3.5.1  Uninstalling Windriver from Windows (95/98/ME/NT/2000)

  1. Uninstall the Windriver service using the command
    windriver\util\wdreg remove

  2. Use the Ädd/Remove Programs" applet from the Control Panel, select Windriver, and click on the "Remove" button.

  3. Delete the following files if they exist:

3.5.2  Uninstalling Windriver from Linux

NOTE: You must be logged in as root to do the uninstallation.

  1. Uninstall the Windriver service

  2. If you created a symbolic link to DriverWizard, delete the link using the command "rm -f /usr/bin/wdwizard"

  3. Delete the Windriver installation directory. Use the command "rm -rf /usr/local/WinDriver"

3.5.3  Uninstalling WinDriver from Solaris

  1. Uninstall the WinDriver service

  2. If you created a symbolic link to DriverWizard, delete the link using the command "rm -f /usr/bin/wdwizard"

  3. Delete the WinDriver installation directory. Use the command "rm -rf /usr/local/WinDriver"

3.5.4  Uninstalling DriverBuilder for Vxworks

  1. Delete the DriverBuilder installation directory (for example C:\DriverBuilder) using Windows Explorer

  2. If you created any shortcuts to DriverWizard on your desktop, delete the shortcut.

Chapter 4
The DriverWizard

4.1  An Overview

DriverWizard (included in the WinDriver toolkit) is a Windows-based diagnostics tool that lets you write to and read from the hardware, before writing a single line of code. The hardware is diagnosed through a Windows interface - memory ranges are read, registers are toggled and interrupts are checked.

Once the card is operating to your satisfaction, DriverWizard creates the skeletal driver source code, creating functions accessing all your hardware resources (where `status register' is a register you have defined on your hardware).

If you are developing a driver for a PLX based card, it is recommended to read the chapter 9 that explains WinDriver's enhanced support for specific PCI chipsets, before starting your driver development(Note: this chapter does not appear in the HLP format documentation). You can use DriverWizard to diagnose your hardware. You should use DriverWizard to generate an INF file for your card for Windows operating systems. You should avoid using DriverWizard to generate code for your PLX card because DriverWizard generates generic code and you will have to modify the code before it can be useful. We supply complete source code libraries and sample applications tailored for various PLX chipsets in the package.

DriverWizard is an excellent tool for two major phases in your HW / Driver development:

  1. Hardware diagnostics: After the hardware has been built, insert the hardware into the PCI/PCMCIA/ISA/ISA PnP/EISA/CompactPCI bus or attach your new USB device to the USB port in your machine, and use DriverWizard to check that the hardware is performing as expected.

  2. Code generation: Once you are ready to build your code, let DriverWizard generate your driver code for you.

The code generated by DriverWizard is composed of the following elements:

4.2  DriverWizard Walkthrough

Following are the five steps in using DriverWizard:

  1. Insert your card in your hardware bus (PCI/PCMCIA/ ISA/ISA PnP/EISA/CompactPCI) or attach your USB device to the USB port in your machine.

  2. Run DriverWizard.

    images/Driver_Wiz_Card_Sel.png

    Figure 4.1: Selection of PnP Device

    To generate an INF file simply press the `Generate .INF file' button in the `Card information' screen and save the generated .INF file (the default name given to the file by DriverWizard is `my_device.inf').

    To install the .INF file follow the instruction displayed by DriverWizard or refer to Section 21.3 that explains how to create an INF file.

    Why should I create an INF file?

    1. To stop the `new hardware wizard' of the Windows operating system from popping up after boot.

    2. In some cases the OS doesn't initialize the PCI configuration registers in Win98/ME and Windows 2000 without an INF file. In such cases, you will not be able to diagnose your hardware with DriverWizard until after creating the INF file.

    3. In some cases the OS doesn't assign physical address to USB devices without an INF file. In these cases you will not be able to diagnose your USB device with DriverWizard until after creating the INF file.

    4. To load the new driver created for the card / device. Creating an INF file is required whenever developing a new driver for the hardware.

    5. To replace the existing driver with a new one.

  3. Configure your USB device (developers working with PCI/PCMCIA/ISA/ISA PnP/EISA/CompactPCI cards should skip this step):

    images/USBDEVCONF.png

    Figure 4.2: USB Device Configuration

  4. Diagnose your device

    A PCI diagnostic screen

    images/PCIDIAG.png

    Figure 4.3: A PCI Diagnostics Screen

    In order to perform data transfers follow the steps given below:

    A USB diagnostics screen

    images/USBDIAG.png

    Figure 4.4: USB Diagnostics Screen

  5. Generate the skeletal driver code.

    images/gencodeop.png

    Figure 4.5: Generate Code Option

    Select the WinDriver option from the `Choose type of driver' screen. Selecting the KernelDriver option will generate kernel source code designed for full kernel mode drivers. See the KernelDriver documentation or the Jungo http://www.jungo.com site url for more details (Note: this screen appears only when both WinDriver and KernelDriver are installed on your machine.)

    images/Type_Of_Driver.png

    Figure 4.6: Select Driver Type

    From the following screen, choose the language in which the code will be generated , and choose your desired development environment for the various operating systems.

    images/GEN_CDE_OPT.png

    Figure 4.7: Options for Generating Code

  6. Compile and run the generated code.

4.3  DriverWizard Notes

4.3.1  Sharing a Resource

When two or more drivers want to share the same resource, you must define that resource as `shared'.

To define a resource as shared:

  1. Select the resource.

  2. `Right click' the resource.

  3. Select `Share' from the menu.

(Note: The default for a newly defined interrupt is `shared'. If you wish to define it as an unshared interrupt, follow steps 1-2 and select `Unshared' from the menu in step 3).

4.3.2  Disabling a Resource

During your diagnostics, you may wish to disable a resource, so that DriverWizard will ignore it, and not create code for it.

To disable a resource

  1. Select the resource.

  2. `Right - click' on the resource name.

  3. Choose `Disable' from the menu.

4.3.3  DriverWizard Logger

DriverWizard Logger is the blank window that opens up along with the device resources dialog when opening a new project. The logger keeps track of all your input / output in the diagnostics stage, so that the developer may analyze his device's physical performance at a later time. It is possible to save the log for future reference. When saving the project, your log is saved as well. Each log is associated with one project.

4.3.4  Automatic Code Generation

After you have finished diagnosing your device and have ensured that it runs according to your specifications, you are ready to write your driver.

Step One ¡- Generating your code.

Choose Generate Code from the Build menu. DriverWizard will generate the source code for your driver, and place it along with the project file (xxx.wdp where xxx is your project name). The files are saved in a directory the DriverWizard creates for every development environment and operating system chosen in the `Generate Code' screen.

In the source code directory you now have a new `xxxlib.h' file which states the interface for the new functions that DriverWizard created for you, and the source of these functions `xxxlib.c', where your device specific API is implemented. In addition, you will find the sample main() function in the file `xxxdiag.c'.

The code generated by DriverWizard is composed of the following elements and files (`xxx' ¡ your project name):

  1. Library functions for accessing each element of your card's resources (Memory ranges, I/O ranges, registers, interrupts and the USB pipes).

    xxx_lib.c ¡ Here you can find the implementation of your hardware specific API, (found in xxx_lib.h), using the regular WinDriver API.

    xxx_lib.h ¡ This is the header file of the diagnostics program. Here you can find all your hardware specific API created by DriverWizard. You should include this file in your source code to use this API.

  2. A general PCI utility library.

    A diagnostics program, which is a console application with which you can diagnose your card. This application utilizes the special library functions, which were created for your device by DriverWizard. Use this diagnostics program as your skeletal device driver.

    pci_diag_lib.c ¡ This is the source code of the diagnostics program DriverWizard creates.

  3. A list of all files created can be found at xxx_files.txt.

After creating your code, compile it with your favorite Win32 compiler, and see it work!

Change the function main() of the program so that the functionality fits your needs.

Step 2 - Compiling the generated code

For Windows 95, 98, ME, NT, 2000 and CE (Using MSDEV)

For Windows platforms, DriverWizard generates the project files (for MSDEV 4, 5 and 6 ,C Builder and Delphi 2, 3, 4). After code generation, the chosen IDE (Integrated development environment) will be launched automatically. You can immediately compile and run the generated code.

For Linux and Solaris

DriverWizard creates a makefile for your project.

Compile the source code using the makefile generated by DriverWizard.

Use GCC to build your code.

For Other OSs or IDEs

Create a new project in your IDE (Integrated development environment).

Include the source files created by DriverWizard into your project.

Compile and run the project.

The project contains a working example of the custom functions that DriverWizard created for you. Use this example to create the functionality you want.

4.4  Remote WinDriver

Remote WinDriver enables driver developers and hardware engineers to develop PCI/PCMCIA/ISA/ISA PnP/EISA/CompactPCI and USB based device drivers on any remote target including embedded systems. Remote WinDriver accesses the hardware on the remote target system from a host machine, tests it and generates a driver for it.

Remote WinDriver consists of two components:

  1. The client component which is integrated into DriverWizard.

  2. The server component (WinDriver Remote Agent) which runs on the target system and communicates with one or more clients over the TCP/IP network.

Remote WinDriver clients are available on Windows, Linux and Solaris. In version 5.0, server components are supplied for Windows(95/98/ME/NT/2000/CE), Linux, Solaris and VxWorks.

4.4.1  How does it work?

Remote WinDriver utilizes a TCP/IP connection to communicate between the host development system and a target development system to which the hardware is plugged. On the remote target machine, WinDriver Remote Agent is installed together with the WinDriver Kernel Module, and permits access to the hardware directly from the user level in the local host development machine.

4.4.2  Using Remote WinDriver (For all Windows OSes)

Notes: By default, wdremote_gui.exe listens to the TCP port number 1701 for incoming remote connections. If this port number is already used on your system, you can change the port number to a different one that you know is unused. If you do so, then on the host-side, you need to specify the same port number when you attempt to connect using DriverWizard.

There are two ways of running this program wdremote_gui.exe:

  1. Just run it from the command prompt as wdremote_gui.exe

  2. Launch the program from Programs | WinDriver | WinDriver Remote Server

4.4.3  Using Remote WinDriver (For Windows CE)

Remote WinDriver configuration for Windows CE is different from other OSes. The following steps explain the Remote WinDriver Setup and use for Win CE.

  1. Copy wdremote.dll (in \WinDriver\util) to the windows directory of your hand-held PC. If your WinCE target is a normal PC, then copy wdremote.dll to the release directory of WinCE in your NT workstation

  2. Establish a serial PC link between the host and the target using Microsoft Active Sync

  3. Run DriverWizard on the NT workstation

  4. Open a new project

  5. DriverWizard will display all the hardware components in the local machine

  6. Click the ``Remote Machine'' button

  7. The following screen is then displayed:

    images/remote_wiz.png

    Figure 4.9: Remote WinDriver on Windows CE

  8. Select the Protocol Type as Wince COM

  9. Click the Connect button

  10. After a few seconds, the hardware in the Remote Machine is displayed. If you are using an eval version , then as soon as the remote connection is established, a screen pops-up in the target machine informing you that the WinDriver kernel will work only for 30 mins. After you click OK in this screen, DriverWizard will display the list of devices on the target platform.

    In the eval versions, the remote agent in the CE machine will stop working after 10 mins.

  11. Select your hardware and click OK

  12. Test, diagnose and generate code for your driver as with any other WinDriver product.

For more information, refer to Chapters 4 and 5 that illustrate the process of developing a device driver.

4.4.4  Using Remote WinDriver (For Linux and Solaris)

Note: wdremote is a command line program that takes one option - the TCP port number to listen on. If no option is given, the port number defaults to 1701.

Just run this program as wdremote from the command line. Call this program from /etc/rc.d/rc.local/ to have it started automatically at boot time.

4.4.5  Using Remote WinDriver (For VxWorks)

For more information, refer to Chapters 4 and 5 that illustrate the process of developing a device driver.

Chapter 5
Creating Your Driver

This chapter takes you through the WinDriver driver development cycle.

IMPORTANT NOTE: If your card's PCI bridge is either a PLX, Altera, PLDA, Galileo, QuickLogic, AMCC or V3, then WinDriver's special chip-set APIs dramatically shorten your development time. If this is the case, read the following overview, and jump straight to the chapter discussing this or refer to the electronic reference manual.

5.1  Using the DriverWizard to Build a Device Driver

See Chapter 7 that details the function reference for WinDriver , Chapter 8 that details the structure reference for WinDriver and Chapter 10 that explains the WinDriver Implementation Issues for more details.

5.2  Writing the Device Driver without the DriverWizard

  1. Copy the file windrvr.h to your source code directory.

  2. Add these lines to the source code:

#include <windows.h>
#include <winioctl.h>
#include "windrvr.h"

  1. Call WD_Open() at the beginning of your program to get a handle for WinDriver.

  2. Call WD_Version() to make sure that the WinDriver version installed is up to date.

  3. For PCI cards: call WD_PciScanCards() to get a list of the PCI cards installed. Choose your card and call WD_PciGetCardInfo()

  4. For ISA Plug and Play (PnP) cards: call WD_IsapnpScanCards() to get a list of the ISA PnP cards installed. Choose your card and call WD_IsapnpGetCardInfo().

  5. For ISA (non PnP) cards: fill in your card information (IO, memory & interrupts) in the WD_CARD structure.

  6. For PCMCIA Cards: call WD_PcmciaScanCards() to get a list of the PCMCIA cards installed. Choose your card and call WD_PcmciaGetCardInfo().

Note: WD_ PcmciaGetCardInfo() inserts an ITEM_BUS item as the first element of the WD_ITEMS array of the WD_CARD structure that it returns. This item must be present for PCMCIA card configuration to work correctly. If you are filling up the WD_CARD structure yourself without the help of WD_ PcmciaGetCardInfo(), then you must set up this item yourself and it must be the first entry in the WD_ITEMS array.

  1. For USB devices ¡ call WD_UsbScanDevice() to get the unique ID of your device.

  2. For USB devices ¡ an optional step is to call WD_UsbGetConfiguration() to learn about your device configurations and interfaces.

  3. Call WD_CardRegister(). For USB devices call WD_UsbDeviceRegister() instead, to open a handle to your device with the desired configuration.

  4. Now you can use WD_Transfer() to perform IO and memory transfers or operate your USB device by calling WD_UsbTransfer().

  5. For PCI/PCMCIA/ISA/ISA PnP/EISA/CompactPCI cards: If the card uses interrupts call WD_IntEnable(). Now you can wait for interrupts using WD_IntWait().

  6. To finish call WD_CardUnregister() or WD_USBDeviceUnregister() for your USB device, and at the end call WD_Close().

5.3  Win CE - Testing on CE

Emulation

WinDriver is currently the only tool that enables you to test your driver code with your hardware on your NT machine ¡ under the CE emulation environment. This can dramatically shorten your development time by eliminating the need to work via a serial cable each time you want to see how your driver code operates your hardware.

If your NT host development workstation already has the target hardware plugged in, you can use the X86 HPC software emulator to test your driver. You need to generate the code as usual using DriverWizard, or from scratch as described earlier in this chapter. When compiling the code, select the target platform as X86em from the VisualC++ WCE Configuration Toolbar. You will need to link the import library windriver\redist\register\x86emu\windrvr_ce_emu.lib with your application program objects.

5.4  Using the Help Files

You may use the help files supplied to you with the WinDriver toolkit. Use these files by pressing `Start' on your task bar, and choosing `Programs | WinDriver | WinDriver Help' from there.

Chapter 6
Debugging

Debugging your hardware access application code should be approached in the manner described in the following sections

6.1  User Mode Debugging

  1. WINCE210\PUBLIC\COMMON\DDK\INC\DBGPRINT.H

  2. \WINCE210\PUBLIC\COMMON\OAK\DEMOS\DBGSAMP1

6.2  DebugMonitor

DebugMonitor is a powerful graphical and console mode tool for monitoring all activities handled by the WinDriver Kernel (windrvr.sys/ windrvr.vxd / windrvr.dll / windrvr.o / wdpnp.sys). Using this tool you can monitor how each command sent to the kernel is executed.

6.2.1  Using DebugMonitor

DebugMonitor has two modes ¡ Graphic and Console mode. The following is an explanation on how to operate DebugMonitor in both modes.

6.2.1.1  DebugMonitor ¡ Graphical Mode

Applicable for Windows 95, 98, ME, NT, 2000. You may also use DebugMonitor to debug your CE driver code running on CE emulation on Windows NT. For Linux, Solaris, VxWorks and CE targets use the console mode DebugMonitor.

images/common6.png

Figure 6.1: Start DebugMonitor

images/common7.png

Figure 6.2: Set Trace Options

Status - Set trace on or off.

Section - Choose what part of the WinDriver API you are interested to monitor. If you are developing a PCI card and experiencing problems with your interrupt handler you should check the Int box and the PCI box.

Checking more options than necessary could amount to overflow of information making it harder for you to locate your problem. USB developers, should choose the USB box.

The Ker_drv option is for KernelDriver users, monitoring communication between their custom Kernel mode drivers (developed using KernelDriver) and the WinDriver kernel.

Level - Choose the level of messages you are interested to see for the resources defined. Error is the lowest level of trace, resulting with minimum output to the screen. Trace is the highest level of tracing displaying every operation the WinDriver Kernel performs.

Once you have defined what you want to trace and on what level just press OK to close the ``Modify status'' window, activate your program, (Step by step or in one run), and watch the monitor screen for error or any unexpected messages.

6.2.1.2  DebugMonitor - Console Mode

This tool is available in all operating systems supported including Linux. To use it run ``wddebug'' in the \WinDriver\util\ directory with the appropriate switches. For a list of switches available with the DebugMonitor in console mode just type ``wddebug'' and a help screen appears, describing all the different options for this command.

To see activity logged with the DebugMonitor simply type ``wddebug dump''.

6.2.1.3  DebugMonitor on Linux and Solaris

On Linux and Solaris, DebugMonitor is only available in console mode. Its usage is therefore as described in Section 6.2.1.2. However, you can also start it from DriverWizard GUI using the menu selection Tools | Debug Monitor. This starts up seperate Xterm window with the command line verison of wddebug running inside it.

6.2.1.4  DebugMonitor on Windows CE

On Windows CE, DebugMonitor is only available in console mode. Its usage is therefore as described in Section 6.2.1.2. You first need to start a Windows CE command window (CMD.EXE) on the Windows CE target computer and then run the program WDDEBUG.EXE inside this shell.

6.2.1.5  DebugMonitor on VxWorks

On VxWorks, DebugMonitor is only available in console mode. Its usage is therefore as described in Section 6.2.1.2. However, because of the special syntax of the Tornado WindShell, we show a sample session with Tornado II IDE below, where we first load the debug monitor, then set the options and then run it to capture information.

-> ld < wddebug.out
Loading wddebug.out |
value = 10893848 = 0xa63a18
-> wdddebug

-> wddebug_main "on", "trace", "all"
Debug level (4) TRACE, Debug sections (0xffffffff) ALL , 
Buffer size 16384
value = 0 = 0x0
-> wddebug_main "dump" 
WDDEBUG v5.00 Debugging Monitor.
Running DriverBuilder V5.00 Jungo (c) 2001 evaluation copy
Time: THU JAN 01 01:06:56 2001
OS: VxWorks
Press CTRL-BREAK to exit

Please note the following:

Chapter 7
WinDriver Function Reference

The WinDriver API is available from the user mode and the Kernel Plugin to WinDriver users, and from the kernel mode for KernelDriver users.

Use this Chapter as a quick reference to the WinDriver functions. The definition of the structures used in the following functions may be found in the `WinDriver Structure Reference' [8].

NOTE: If you are a registered user, you need to read the file register.txt under windriver/redist/register or kerneldriver/redist/register to understand the process of enabling your driver to work with the registered version.

7.1  WD_Open()

Open a WinDriver device and return a handle to the device. WD_Open must be called before any other WinDriver functions can be used.

NOTE: If you are a registered user, you need to read the file register.txt under windriver/redist/register or kerneldriver/redist/register to understand the process of enabling your driver to work with the registered version.

Prototype

HANDLE WD_Open();

Return Value

INVALID_HANDLE_VALUE if device could not be opened, otherwise returns the handle.

Example

HANDLE hWD;

hWD = WD_Open();
if (hWD==INVALID_HANDLE_VALUE)
{
 printf ("Cannot open WinDriver device\n");
}

7.2  WD_Close()

Closes the WinDriver device. This must be called when finished using the driver.

Prototype

void WD_Close(HANDLE hWD);

Parameters

hWD - handle of driver from WD_Open()

Example

WD_Close (hWD); 

7.3  WD_Version()

Returns the version of WinDriver that is currently running.

Prototype

void WD_Version( HANDLE hWD, WD_VERSION *pVer);

Parameters(WD_VERSION elements)

Example

WD_VERSION ver;

BZERO(ver);
WD_Version (hWD, &ver);
printf("%s\n", ver.cVer);
if (ver.dwVer <WD_VER)
{
 printf ("error incorrect WinDriver version \n");
}

7.4  WD_PciScanCards()

Scan the PCI bus for cards installed.

Prototype

void WD_PciScanCards( HANDLE hWD, WD_PCI_SCAN_CARDS *pPciScan);

Parameters(WD_PCI_SCAN_CARDS elements)

Example

WD_PCI_SCAN_CARDS pciScan; 
DWORD cards_found;
WD_PCI_SLOT pciSlot;

BZERO(pciScan);
pciScan.searchId.dwVendorId = 0x12bc;
pciScan.searchId.dwDeviceId = 0x1;
WD_PciScanCards (hWD, &pciScan);
if (pciScan.dwCards>0) // Found at least one card
{
 pciSlot = pciScan.cardSlot[0];
}
else
{
 printf ("No matching PCI cards found\n");
}

7.5  WD_PciGetCardInfo()

Get PCI card information: interrupts, I/O & memory.

Prototype

BOOL WD_PciGetCardInfo(HANDLE hWD, WD_PCI_CARD_INFO *pPciCard);

Parameters(WD_PCI_CARD_INFO elements)

Example

WD_PCI_CARD_INFO pciCardInfo;
WD_CARD Card;

BZERO(pciCardInfo);
pciCardInfo.pciSlot = pciSlot;
WD_PciGetCardInfo (hWD, &pciCardInfo);
if (pciCardInfo.Card.dwItems!=0)
{ 
 Card = pciCardInfo.Card;
}
else 
{
 printf ("Failed fetching PCI card information\n");
}

7.6  WD_PciConfigDump()

Read / Write the PCI configuration registers.

Prototype

void WD_PciConfigDump( HANDLE hWD, WD_PCI_CONFIG_DUMP *pConfig);

Parameters(WD_PCI_CONFIG_DUMP elements)

PCI_ACCESS_OK - if read/write ok
PCI_ACCESS_ERROR - if error
PCI_BAD_BUS - if bus doesn't exist
PCI_BAD_SLOT - if slot and function don't exist

Example

WD_PCI_CONFIG_DUMP pciConfig;
WORD aBuffer[2]; 

BZERO(pciConfig);
pciConfig.pciSlot.dwBus = 0;
pciConfig.pciSlot.dwSlot = 3;
pciConfig.pciSlot.dwFunction = 0;
pciConfig.pBuffer = aBuffer;
pciConfig.dwOffset = 0; 
pciConfig.dwBytes = sizeof(aBuffer);
pciConfig.fIsRead = TRUE; 

WD_PciConfigDump( hWD, &pciConfig);
if (pciConfig.dwResult!=PCI_ACCESS_OK)
{
 printf ("No PCI card in Bus 0 Slot 3\n"); 
}
else
{
 printf ("Card in Bus 0 Slot 3 has VendorID %x DeviceID %x" ,
           aBuffer[0], aBuffer[1]);
}

7.7  WD_PcmciaScanCards()

Scans the PCMCIA bus for PCMCIA cards installed.

Prototype

BOOL WD_PcmciaScanCards(HANDLE hWD, WD_PCMCIA_SCAN_CARDS
                       *pBuf); 

Parameters(WD_PCMCIA_SCAN_CARDS elements)

Example

WD_PCMCIA_SCAN_CARDS pcmciaScan;
DWORD cards_found;
WD_PCMCIA_CARD pcmciaCard;

BZERO(pcmciaScan);
// Kingston DATAFLASH ATA Flash Card }
strcpy (pcmciaScan.searchId.cManufacturer, "Kingston Technology"); 
strcpy (pcmciaScan.searchId.cProductName, "DataFlash");

WD_PcmciaScanCards (hWD, &pcmciaScan);

if (pcmciaScan.dwCards > 0) // Found at least one card
{
 pcmciaCard = pcmciaScan.Card[0];
}
else
{
 printf ("No matching PCMCIA cards found");
}

7.8  WD_PcmciaGetCardInfo()

Get PCMCIA card information: interrupts, I/O & memory.

Prototype

BOOL WD_PcmciaGetCardInfo(HANDLE hWD, WD_PCMCIA_CARD_INFO pPcmciaCard);

Parameters(WD_PCMCIA_CARD_INFO elements)

Example

WD_PCMCIA_CARD_INFO pcmciaCardInfo;
WD_CARD Card;

BZERO(pcmciaCardInfo);

// get this from WD_PcmciaScanCards()

pcmciaCardInfo.pcmciaSlot = pcmciaSlot;

WD_PcmciaGetCardInfo (hWD, &pcmciaCardInfo);

if (pcmciaCardInfo.Card.dwItems!=0) 
{
 Card = pcmciaCardInfo.Card;
}
else
{
 printf ("Failed fetching PCMCIA card information\n");
}

7.9  WD_PcmciaConfigDump()

Read/ Write the PCMCIA configuration registers.

Prototype

void WD_PcmciaConfigDump( HANDLE hWD, WD_PCMCIA_CONFIG_DUMP *pConfig);

Parameters(WD_PCMCIA_CONFIG_DUMP elements)

7.10  WD_IsapnpScanCards()

Scan the ISA bus for ISA Plug and Play cards installed.

Prototype

void WD_IsapnpScanCards( HANDLE hWD, WD_ISAPNP_SCAN_CARDS *pIsapnpScan);

Parameters(WD_ISAPNP_SCAN_CARDS elements)

Example

WD_ISAPNP_SCAN_CARDS isapnpScan;
DWORD cards_found;
WD_ISAPNP_CARD isapnpCard;

BZERO(isapnpScan);
// CTL009e - Sound Blaster ISA PnP card
strcpy (isapnpScan.searchId.cVendorId, "CTL009e");
isapnpScan.searchId.dwSerial = 0;
WD_IsapnpScanCards (hWD, &isapnpScan); 
if (isapnpScan.dwCards>0) // Found at least one card 
{
 isapnpCard = isapnpScan.Card[0];
}
else
{
 printf ("No matching ISA PnP cards found\n");
}

7.11  WD_IsapnpGetCardInfo()

Get ISA Plug and Play card information: interrupts, I/O & memory.

Prototype

BOOL WD_IsapnpGetCardInfo(HANDLE hWD, WD_ISAPNP_CARD_INFO *pIsapnpCard);

Parameters(WD_ISAPNP_CARD_INFO elements)

Example

WD_ISAPNP_CARD_INFO isapnpCardInfo;
WD_CARD Card;

BZERO(isapnpCardInfo);
// from WD_IsapnpScanCard():
isapnpCardInfo.CardId = isapnpCard;
isapnpCardInfo.dwLogicalDevice = 0;
WD_IsapnpGetCardInfo (hWD, &isapnpCardInfo);
if (isapnpCardInfo.Card.dwItems!=0)
{
 Card = isapnpCardInfo.Card; 
}
else
{
 printf ("Failed fetching ISA PnP card information\n");
}

7.12  WD_IsapnpConfigDump()

Read / Write the ISA PnP configuration registers.

Prototype

void WD_IsapnpConfigDump( HANDLE hWD, WD_ISAPNP_CONFIG_DUMP *pConfig);

Parameters(WD_ISAPNP_CONFIG_DUMP elements)

ISAPNP_ACCESS_OK - if read/write ok
ISAPNP_ACCESS_ERROR - if error
ISAPNP_BAD_ID - if card does not exist

Example

WD_ISAPNP_CONFIG_DUMP isapnpConfig;

BZERO(isapnpConfig);
// from WD_IsapnpScanCard():
isapnpConfig.CardId = isapnpCard;
isapnpConfig.dwOffset = 0;
isapnpConfig.fIsRead = TRUE; 
WD_IsapnpConfigDump( hWD, &isapnpConfig);
if (isapnpConfig.dwResult!=ISAPNP_ACCESS_OK)
{
 printf ("No ISA PnP card specified slot\n");
}
else
{
 printf ("ISA PnP config in offset 0 =%x", 
           isapnpConfig.bData); 
}

7.13  WD_CardRegister()

Register card - install interrupts & map card memory. For USB devices, see WD_UsbDeviceRegister.

Must be called in order to use interrupts and perform I/O & memory transfers to card.

Prototype

void WD_CardRegister(HANDLE hWD, WD_CARD_REGISTER *pCardReg);

Parameters(WD_CARD_REGISTER elements)

FOR AN I/O RANGE ITEM

FOR A MEMORY RANGE ITEM

Card.Item[i].I.Mem.dwPhysicalAddr- first address of physical memory range.

Card.Item[i].I.Mem.dwBytes - length of range in bytes.

Card.Item[i].I.Mem.dwTransAddr - returns the base address to use for memory transfers with WD_Transfer() .

Card.Item[i].I.Mem.dwUserDirectAddr- returns the base address to use for memory transfers directly by user.

FOR AN INTERRUPT ITEM

Example

WD_CARD Card;
WD_CARD_REGISTER cardReg;

// the info for Card comes from WD_PciGetCardInfo() 
// for PCI cards. 
//For ISA cards the information has to be set by the user 
//(IO/memory address & interrupt number).
BZERO(cardReg);
cardReg.Card = Card;
cardReg.fCheckLockOnly = FALSE; 
WD_CardRegister (hWD, &cardReg);
if (cardReg.hCard==0)
 printf ("could not lock device - already in use\n");

7.14  WD_CardUnregister()

Un-register a card, and free its resources. For USB devices see WD_UsbDeviceUnregister().

Prototype

void WD_CardUnregister(HANDLE hWD, WD_CARD_REGISTER *pCardReg);

Parameters(WD_CARD_REGISTER elements)

hCard - handle of card to un-register.

Example

WD_CardUnregister (hWD, &cardReg);

7.15  WD_Transfer()

Execute a read/write instruction to I/O port or memory. For USB devices, see WD_UsbTransfer()

Prototype

void WD_Transfer(HANDLE hWD, WD_TRANSFER *pTrans);

Parameters(WD_TRANSFER elements)

FOR SINGLE TRANSFER

FOR STRING TRANSFER

Example

WD_TRANSFER Trns;
BYTE read_data;

BZERO(Trns);
Trns.cmdTrans = RP_BYTE; // Read Port BYTE
Trns.dwPort = 0x210;
WD_Transfer (hWD, &Trns);
read_data = Trns.Data.Byte;

7.16  WD_MultiTransfer()

Perform multiple I/O & memory transfers.

Prototype

void WD_MultiTransfer(HANDLE hWD, WD_TRANSFER *pTransArray, DWORD dwNumTransfers);

Parameters

Example

WD_TRANSFER Trns[4]; 
DWORD dwResult;
char *cData ="Message to send\n"; 

BZERO(Trns);
Trns[0].cmdTrans = WP_WORD;  // Write Port Word 
Trns[0].dwPort = 0x1e0;
Trns[0].Data.Word = 0x1023;

Trns[1].cmdTrans = WP_WORD;
Trns[1].dwPort = 0x1e0;
Trns[1].Data.Word = 0x1022;

Trns[2].cmdTrans = WP_SBYTE;// Write Port String Byte
Trns[2].dwPort = 0x1f0;
Trns[2].dwBytes = strlen(cData);
Trns[2].fAutoinc = FALSE;
Trns[2].dwOptions = 0;
Trns[2].Data.pBuffer = cData;

Trns[3].cmdTrans = RP_DWORD;// Read Port DWord 
Trns[3].dwPort = 0x1e4;

WD_MultiTransfer(hWD, Trns, 4);
dwResult = Trans[3].Data.Dword;

7.17  WD_IntEnable()

Enable interrupt processing.

Note: The easiest way to handle interrupts with WinDriver is by defining the Interrupt in DriverWizard, and letting DriverWizard generate the code for you. (In Plug-n-Play cards, DriverWizard will auto-detect the interrupts for you).

Prototype

void WD_IntEnable( HANDLE hWD, WD_INTERRUPT *pInterrupt);

Parameters(WD_INTERRUPT elements)

Example

WD_INTERRUPT Intrp;
WD_CARD_REGISTER cardReg;

BZERO(cardReg);
cardReg.Card.dwItems = 1;
cardReg.Card.Item[0].item = ITEM_INTERRUPT;
cardReg.Card.Item[0].fNotSharable = TRUE;
cardReg.Card.Item[0].I.Int.dwInterrupt = 10; // IRQ 10 
 // INTERRUPT_LEVEL_SENSITIVE - set to level sensitive 
 // interrupts, otherwise should be 0. 
 // ISA cards usually are edge sensitive, and PCI cards 
 // usually are level sensitive.
cardReg.Card.Item[0].I.Int.dwOptions = 
      INTERRUPT_LEVEL_SENSITIVE;
cardReg.fCheckLockOnly = FALSE;
WD_CardRegister (hWD, &cardReg);
if (cardReg.hCard==0)
    printf("could not lock device - already in use\n");
else
{
 BZERO(Intrp); 
 Intrp.hInterrupt = 
     cardReg.Card.Item[0].I.Int.hInterrupt;
 Intrp.Cmd = NULL;
 Intrp.dwCmds = 0;
 Intrp.dwOptions = 0;
 WD_IntEnable(hWD, &Intrp); 
}
if (!Intrp.fEnableOk) 
   printf("failed enabling interrupt\n");
}

7.18  WD_IntDisable()

Disable interrupt processing.

Prototype

void WD_IntDisable( HANDLE hWD, WD_INTERRUPT *pInterrupt);

Parameters(WD_INTERRUPT elements)

hInterrupt - handle of interrupt to disable.

Example

WD_IntDisable(hWD, &Intrp);

7.19  WD_IntWait()

Wait for an interrupt.

Prototype

void WD_IntWait( HANDLE hWD, WD_INTERRUPT *pInterrupt);

Parameters(WD_INTERRUPT elements)

Example

for (;;)
{
  WD_IntWait (hWD, &Intrp);
  if (Intrp.fStopped)
     break;

ProcessInterrupt (Intrp.dwCounter);
}

7.20  WD_IntCount()

Count the number of interrupts from the time WD_IntEnabled was called.

Prototype

void WD_IntCount( HANDLE hWD, WD_INTERRUPT *pInterrupt);

Parameters(WD_INTERRUPT elements)

Example

DWORD dwNumInterrupts;

WD_IntCount (hWD, &Intrp); 
dwNumInterrupts = Intrp.dwCounter;

7.21  WD_DMALock()

Lock a linear memory region, and return a list of the corresponding physical addresses.

Prototype

void WD_DMALock( HANDLE hWD, WD_DMA *pDma);

Parameters(WD_DMA elements)

  1. Set to DMA_KERNEL_BUFFER_ALLOC so WinDriver will allocate a contiguous buffer. When this option is set, the user address of the buffer will be returned in pUserAddr. Use this option if your device does not support scatter/gather transfers.
  2. Set to DMA_LARGE_BUFFER for locking down regions larger than 1MB (See `Implementing DMA' for more details).

Example 1

User buffer DMA (scatter gather locking)

WD_DMA Dma;
PVOID pBuffer = malloc (20000);

BZERO(Dma);
Dma.dwBytes = 20000;
Dma.pUserAddr = pBuffer;
Dma.dwOptions = 0;
WD_DMALock (hWD, &Dma);
// on return Dma.Page has the list of physical addresses
if (Dma.hDma==0)
 printf ("Could not lock down buffer\n");

Example 2

The following code shows kernel buffer DMA

BZERO(Dma)
Dma.dwBytes =20 * 4096; //(20 pages)
Dma.dwOptions=DMA_KERNEL_BUFFER_ALLOC;
{
WD_DMALock (hWD, &Dma);
// on return Dma.Page has the list of physical addresses
if (Dma.hDma==0)
 printf("Failed allocating kernel buffer for DMA\n");

7.22  WD_DMAUnlock()

Unlock a DMA buffer.

Prototype

void WD_DMAUnlock( HANDLE hWD, WD_DMA *pDma);

Parameters(WD_DMA elements)

hDma - handle for DMA buffer to unlock.

Example

WD_DMAUnlock (hWD, &Dma);

7.23  WD_Sleep()

Delay execution for a specific amount of time. This function is used when accessing slow hardware.

Prototype

void WD_Sleep( HANDLE hWD, WD_SLEEP *pSleep);

Parameters(WD_Sleep elements)

Example

WD_SLEEP sleep;

BZERO (sleep);
sleep.dwMicroSeconds = 1000; // Sleep for 1 millisecond
sleep.dwOptions = 0;
WD_Sleep (hWD, &sleep);

7.24  WD_UsbScanDevice()

Scan the USB tree for installed devices.

Prototype

void WD_UsbScanDevice(Handle hWD, WD_USB_SCAN_DEVICES *pScan);

Parameters(WD_USB_SCAN_DEVICES elements):

Example

WD_USB_SCAN_DEVICES scan;
DWORD uniqueId;

BZERO(scan);
scan.searchId.dwVendorId = 0x553;
scan.searchId.dwProductId = 0x2;
WD_UsbScanDevice(hWD, &scan);
if (scan.dwDevices > 0) // Found atleast one card
{
 uniqueId = scan.uniqueId[0];
}
else
{
 printf("No matching USB devices found\n");
}

7.25  WD_UsbGetConfiguration()

Get information about a USB device.

Prototype

void WD_UsbGet Configuration(HANDLE hWD, WD_USB_CONFIGURATION *pConfig);

Parameters(WD_USB_CONFIGURATION elements)

Example

WD_USB_CONFIGURATION config;

BZERO(config);
config.uniqueId=2;
config.dwConfigurationIndex=0;
WD_UsbGetConfiguration(hWD, &config);
printf("found %d interfaces\n", 
         config.dwInterfaceAlternatives);

7.26  WD_UsbDeviceRegister()

Register the selected interface of the device. (This tells the hardware which interface to work with).

Must be called in order to perform data transfers on the pipes.

Prototype

void WD_UsbDeviceRegister(HANDLE hWD, WD_USB_DEVICE_REGISTER *pDevice);

Parameters (WD_USB_DEVICE_REGISTER elements)

Example

WD_USB_DEVICE_REGISTER device;

BZERO(device);
device.uniqueId = 2;
device.dwConfigurationIndex = 0;
device.dwInterfaceNum = 1;
device.dwInterfaceAlternative = 1;
WD_DeviceRegister(hWD, &device);
if(!device.{hDevice}
 printf("error - could not register device\n");
else
 printf("device has %d pipes\n", device.Device.dwPipes);

7.27  WD_UsbDeviceUnregister()

Un-register the device.

Prototype

void WD_UsbDeviceUnregister(HANDLE hWD, WD_USB_DEVICE_REGISTER
                            *pDevice);

Parameters(WD_USB_DEVICE_REGISTER elements)

hDevice - the handle of the device to un-register

Example

WD_UsbDeviceUnregister(hWD, &Device);

7.28  WD_UsbTransfer()

Perform Read / Write data transfers from / to the device using it's pipes.

Prototype

void WD_UsbTransfer(HANDLE hWD, WD_USB_TRANSFER *pTrans);

Parameters(WD_USB_TRANSFER elements)

Example

WD_USB_TRANSFER trans;

BZERO(trans);
trans.hDevice = hDevice;
trans.dwPipe = 0x81;
trans.fRead = TRUE;
trans.pBuffer = malloc(100);
trans.dwBytes = 100;
WD_UsbTransfer(hWD, &trans);
if (!fOK)
 printf("Error on Transfer\n");
else
 printf("Transferred %d bytes from %d\n", 
          trans.dwBytesTransferred,trans.dwBytes);

7.29  WD_UsbResetPipe()

Reset the pipe to its default state (resets the state machine of the firmware's pipe to its initial state)

Prototype

void WD_UsbResetPipe(HANDLE hWD, WD_USB_RESET_PIPE *pReset);

Parameters(WD_USB_RESET_PIPE elements)

Example

WD_USB_RESET_PIPE reset;

BZERO(reset);
reset.hDevice = hDevice;
reset.dePipe = 0x81;
WD_UsbResetPipe(hWD, &reset);

7.30  InterruptThreadEnable()

Convenience function for setting up interrupt handling. This function is implemented as a static function in the header file windrvr_int_thread.h found under windriver/include

Prototype

BOOL InterruptThreadEnable(HANDLE *phThread, HANDLE hWD, WD_INTERRUPT *pInt, HANDLER_FUNC func, PVOID pData)

Parameters

Example

VOID interrupt_handler (PVOID pData)
{
    WD_INTERRUPT * pIntrp = (WD_INTERRUPT *) pData;
    // do your interrupt routine here 
    printf ("Got interrupt %d\n", pIntrp->dwCounter);
}

....
main()
{
    WD_CARD_REGISTER cardReg;
    WD_INTERRUPT Intrp;
    HANDLE hWD, thread_handle;
    
    ...
    hWD = WD_Open();
    BZERO(cardReg);
    cardReg.Card.dwItems = 1;
    cardReg.Card.Item[0].item = ITEM_INTERRUPT;
    cardReg.Card.Item[0].fNotSharable = TRUE;
    cardReg.Card.Item[0].I.Int.dwInterrupt = MY_IRQ;
    cardReg.Card.Item[0].I.Int.dwOptions = 0;
    ...
    WD_CardRegister (hWD, &cardReg);
    ...
    PVOID  pData = NULL;
    BZERO(Intrp);
    Intrp.hInterrupt = cardReg.Card.Item[0].I.Int.hInterrupt;
    Intrp.Cmd = NULL;
    Intrp.dwCmds = 0;
    Intrp.dwOptions = 0;
    printf ("starting interrupt thread\n");
    pData = &Intrp;
    if (!InterruptThreadEnable(&thread_handle, hWD, &Intrp, 
         interrupt_handler, pData))
      {
        printf ("failed enabling interrupt\n");
      }
    else
      {
        printf ("Press Enter to uninstall interrupt\n");
        fgets(line, sizeof(line), stdin);
        // this calls WD_IntDisable()
        InterruptThreadDisable(thread_handle);
      }
    WD_CardUnregister(hWD, &cardReg);
    ....
}

7.31  InterruptThreadDisable()

Convenience function for shutting down interrupt handling. This function is implemented as a static function in the header file windrvr_int_thread.h found under windriver/include

Prototype

VOID InterruptThreadDisable(HANDLE hThread)

Parameters

Example

main()
{
    ....
    if (!InterruptThreadEnable(&thread_handle, hWD, &Intrp, 
         interrupt_handler, pData))
      {
	printf ("failed enabling interrupt\n");
      }
    else
      {
       printf ("Press Enter to uninstall interrupt\n");
       fgets(line, sizeof(line), stdin);
       // this calls WD_IntDisable()
       InterruptThreadDisable(thread_handle);
      }
    WD_CardUnregister(hWD, &cardReg);
    ....
}

Chapter 8
WinDriver Structure Reference

The WinDriver API is available from the user mode and the Kernel Plugin to WinDriver users, and from the kernel mode for KernelDriver users. Use this Chapter as a reference to the structures used by the WinDriver API.

8.1  WD_TRANSFER

This structure defines a single transfer operation to be performed by WinDriver.

Used by WD_Transfer() [7.15], WD_MultiTransfer() [7.16], WD_IntEnable() [7.17].

MEMBERS:

TYPE NAMEDESCRIPTION
DWORDcmdTransTransfer command WD_TRANSFER_CMD
DWORDdwPort i/o port for transfer or user memory address
DWORDdwBytes Number of bytes for string transfer
DWORDfAutoinctransfer from one port/address or use incremental range of addresses
DWORDdwOptionsmust be 0
UnionDatathe data for transfer
UCHARData.Byte Use for byte transfer
USHORTData.Word Use for word transfer
DWORDData.DwordUse for dword transfer
PVOIDData.pBufferUse for string transfer

8.2  WD_DMA

Contains information about a DMA buffer. Used by WD_DMALock() [7.21] and WD_DMAUnlock() [7.22].

MEMBERS:

TYPE NAME DESCRIPTION
DWORDhDmaHandle of DMA buffer
PVOIDpUserAddrBeginning of buffer
DWORDdwBytesSize of buffer
DWORD dwOptionsAllocation options:

Bit masked flag - set to

`0' for no option,

or:

DMA_KERNEL_BUFFER_ALLOC DMA_KBUF_BELOW_16M DMA_LARGE_BUFFER

DWORDdwPages Number of pages in the buffer
WD_DMA_ PAGE [8.3] Page [WD_DMA_ PAGES]Array of pages in the buffer

8.3  WD_DMA_PAGE

MEMBERS:

TYPENAME DESCRIPTION
PVOIDpPhysicalAddrphysical address of page
DWORDdwBytessize of page

8.4  WD_INTERRUPT

Used to describe an interrupt

Used by WD_IntEnable() [7.17], WD_IntDisable() [7.18], WD_IntWait() [7.19], WD_IntCount() [7.20], InterruptThreadEnable() [7.30].

MEMBERS:

TYPENAME DESCRIPTION
DWORDhInterrupt handle of interrupt
DWORDdwOptionsinterrupt options:

Bit masked flag. May be

`0' for no option,

or:

INTERRUPT_LEVEL_SENSITIVE (for level sensitive interrupts) or

INTERRUPT_CMD_COPY(choose this when you need the WinDriver kernel to copy theactions of the read command it has done to acknowledge the interrupt, back to the user mode)

WD_TRANSFER [8.1]*CmdPointer to commands to perform on interrupt
DWORDdwCmdsnumber of commands
WD_KERNEL_ PLUGIN_CALL kpCallkernel plugin call
DWORD fEnableOk`1' if WD_IntEnable() succeeded
DWORD dwCounternumber of interrupts received
DWORD dwLostnumber of interrupts not yet dealt with
DWORDfStoppedwas interrupt disabled during wait

8.5  WD_VERSION

Describes version of WinDriver in use. Used by WD_Version() [7.3].

MEMBERS:

TYPENAMEDESCRIPTION
DWORDdwVerversion
CHARcVer[100]string of version

8.6  WD_CARD_REGISTER

Holds a handle to a registered card.

Used by WD_CardRegister() [7.13], WD_CardUnregister() [7.14].

MEMBERS:

TYPE NAMEDESCRIPTION
WD_CARDCardcard to register
DWORDfCheckLock Onlyonly check if card is lockable, return hCard=1 if OK
DWORDhCardhandle of card

8.7  WD_CARD

Describes the card's resources.

MEMBERS:

TYPENAMEDESCRIPTION
DWORDdwItemsNumber of items in card
WD_ITEMS [8.8]Item [WD_CARD_ ITEMS]Array of items[0...dwItems-1]

8.8  WD_ITEMS

Defines each item (resource) in a card.

MEMBERS:

TYPE NAMEDESCRIPTION
DWORDitem ITEM_TYPE
DWORDfNotSharableIf TRUE, item may not be shared.
unionIItem specific information
structI.MemITEM_MEMORY
DWORD I.Mem.dw PhysicalAddrPhysical address on card
DWORDI.Mem.dwBytesAddress range
DWORD I.Mem.dwTrans AddrReturns the address to pass on to transfer commands
DWORDI.Mem.dwUser DirectAddr Returns the address for direct user read/write
DWORDdwCpuPhysical Addrreturns the CPU physical address of card
struct I.IO ITEM I/O
DWORDI.IO.dwAddrBeginning of I/O address
DWORDI.IO.dwBytesI/O range
structI.IntITEM INTERRUPT
DWORDI.Int.dwInterruptNumber of the interrupt to install
DWORD I.Int.dwOptionsinterrupt options:INTERRUPT_LEVEL_SENSITIVE
DWORDI.Int.hInterruptReturns the handle of the interrupt installed

8.9  WD_SLEEP

Defines a sleep command.

Used by WD_Sleep() [7.23].

MEMBERS:

TYPE NAMEDESCRIPTION
DWORDdwMicro SecondsSleep time in micro seconds - 1/1,000,000 of a second.
DWORDdwOptionsshould be zero

8.10  WD_PCI_SLOT

Defines a physical location of a PCI card.

MEMBERS:

TYPE NAMEDESCRIPTION
DWORDdwBusPCI physical bus number of card
DWORD dwSlotPCI physical slot number of card
DWORDdwFunction PCI function on card

8.11  WD_PCI_ID

Defines the identity of a PCI card.

MEMBERS:

TYPE NAMEDESCRIPTION
DWORDdwVendorId The PCI Vendor ID of the card.
DWORDdwDeviceId The PCI Device ID of the card.

8.12  WD_PCI_SCAN_CARDS

Receives information on cards detected on the PCI bus.

Used by WD_PciScanCards() [7.4].

MEMBERS:

TYPENAMEDESCRIPTION
WD_PCI_ID searchIdIf searchId.dwVendorId==0, then scan all vendor IDs.

If searchId.dwDeviceId==0, then scan all device IDs.

DWORDdwCardsNumber of cards found
WD_PCI_ID [8.11]cardId [WD_PCI_ CARDS]VendorID & DeviceID of cards found
WD_PCI_SLOT [8.10]cardSlot [WD_PCI_ CARDS]PCI slot info of cards found

8.13  WD_PCI_CARD_INFO

Describes a PCI card's resources detected.

Used by WD_PciGetCardInfo() [7.5].

MEMBERS:

TYPENAMEDESCRIPTION
WD_PCI_ SLOT [8.10]pciSlotPCI slot
WD_CARD [8.7]Cardget card parameters for PCI slot

8.14  WD_PCI_CONFIG_DUMP

Defines a read / write command to the PCI configuration registers of a PCI card.

Used by WD_PciConfigDump() [7.6].

MEMBERS:

TYPE NAMEDESCRIPTION
WD_PCI_ SLOT [8.10]pciSlot PCI bus,slot and function number
PVOIDpBufferbuffer for read/write
DWORD dwOffsetoffset in PCI configuration space to read/write from
DWORD dwBytesbytes to read/write from/to buffer

Returns the number of bytes read/written

DWORDfIsRead FALSE - write PCI config

TRUE - read PCI config

DWORDdwResult0 - PCI_ACCESS_OK - read/write ok

1 - PCI_ACCESS_ERROR - error

2 - PCI_BAD_BUS - bus does not exist (read only)

3 - PCI_BAD_SLOT - slot or function does not exist (read only)

8.15  WD_ISAPNP_CARD_ID

Identifies a specific ISA Plug and Play card on the ISA bus.

MEMBERS:

TYPENAMEDESCRIPTION
CHARcVendor [8]Vendor ID
DWORDdwSerialSerial number of card

8.16  WD_ISAPNP_CARD

Information on an ISA Plug and Play card.

MEMBERS:

TYPE NAME DESCRIPTION
WD_ISAPNP_ CARD_ID [8.15] cardIdVendor ID and serial number of card found
DWORDdwLogicalDevicesNumber of logical devices on the card
BYTEbPnPVersionMajorISA PnP version major
BYTEbPnPVersionMinor ISA PnP version minor
BYTEbVendorVersionMajorVendor version major
BYTEbVendorVersionMinor Vendor version minor
WD_ISAPNP_ ANSIcIdentDevice identifier

8.17  WD_ISAPNP_SCAN_CARDS

Used to receive information on cards detected on the ISA PnP bus.

Used by WD_IsapnpScanCards() [7.10].

MEMBERS:

TYPENAMEDESCRIPTION
WD_ISAPNP_ CARD_ID [8.15]searchIdIf searchId.cVendorId[0]==0, then scan all vendor IDs.

If searchId.dwSerial==0, then scan all serial numbers.

DWORDdwCardsNumber of cards found
WD_ISAPNP_ CARD [8.16]Card [WD_ISAPNP_ CARDS]cards found

8.18  WD_ISAPNP_CARD_INFO

Describes an ISA PnP card device's resources detected.

Used by WD_IsapnpGetCardInfo() [7.11]

MEMBERS:

TYPE NAMEDESCRIPTION
WD_ISAPNP_ CARD_ID [8.15]cardIdVendor ID and serial number of card for which information is required
DWORDdwLogicalDeviceNumber of the logical device for which information is requested
WD_ISAPNP_ COMP_ID clogicalDeviceId[8]ascii of logical device id found
DWORDdwCompatibleDevices Number of compatible devices found
WD_ISAPNP_ COMP_ID CompatibleDevice [WD_ISAPNP_COMPATIBLE_ IDS]Compatible device IDs
WD_ISAPNP_ ANSI cIdentIdentity of device
WD_CARD [8.7]CardThe card resource information

8.19  WD_ISAPNP_CONFIG_DUMP

Defines a read / write command to the ISA PnP configuration registers of an ISA PnP card.

Used by WD_IsapnpConfigDump() [7.12].

MEMBERS:

TYPE NAMEDESCRIPTION
WD_ISAPNP_ CARD_ID [8.15]cardId VendorID and serial number of card
DWORDdwOffsetoffset in ISA PnP configuration space to read/write from
DWORDfIsReadif 1, then read ISA PnP config

if 0, then write ISA PnP config

BYTEbData result data of byte read/write
DWORDdwResultISAPNP_ACCESS_RESULT

8.20  WD_PCMCIA_SLOT

Defines a physical location of a PCMCIA card.

MEMBERS:

TYPE NAMEDESCRIPTION
BYTEuSocketSpecifies the socket number (first socket is 0)
BYTEuFunctionSpecifies the function number (first function is 0)

8.21  WD_PCMCIA_ID

Defines the identity of a PCMCIA card.

MEMBERS:

TYPENAMEDESCRIPTION
CHARcVersion[WD_PCMCIA_ VERSION_LEN]The Card's PCMCIA version
CHARcManufacturer [WD_PCMCIA_MANUFA CTURER_LEN]Manufacturer name
CHARcProductName[WD_ PCMCIA_PRODUCT NAME_LEN]Product name
USHORTcCheckSumCard's CRC checksum value

8.22  WD_PCMCIA_SCAN_CARDS

Receives information on cards detected on the PCMCIA bus.

Used by WD_PcmciaScanCards() [7.7] .

MEMBERS:

TYPENAMEDESCRIPTION
WD_PCMCIA_ ID [8.21]searchIdif strlen(searchId.cManufacturer) ==0, then scan all Manufacturers.

if strlen(searchId.cProductName) ==0, then scan all product names.

DWORDdwCardsNumber of cards found
WD_PCMCIA_ ID [8.21]cardId[WD_ PCMCIA_ CARDS]Manufacturer Name, Product Name, Version and CRC Info of card found
WD_PCMCIA_ SLOT [8.20]cardSlot[WD_ PCMCIA_ CARDS]PCMCIA slot/function info of cards found

8.23  WD_PCMCIA_CARD_INFO

Describes a PCMCIA card's resources detected.

Used by WD_PcmciaGetCardInfo() [7.8].

MEMBERS:

TYPENAMEDESCRIPTION
WD_PCMCIA_ SLOT [8.20]pcmciaSlotPCMCIA slot information
WD_CARD [8.7]Cardget card parameters for PCMCIA slot

8.24  WD_ PCMCIA_CONFIG_DUMP

Defines a read / write command to the PCMCIA configuration registers of a PCMCIA card.

Used by WD_PcmciaConfigDump() [7.6].

MEMBERS:

TYPE NAMEDESCRIPTION
WD_ PCMCIA_ SLOT [8.20]pcmciaSlotSlot descriptor of PCMCIA card
PVOIDpBufferbuffer for read/write
DWORDdwOffsetoffset in pcmcia PnP configuration space from which to read/write
DWORDdwBytesbytes to read from or write to buffer

Returns the number of bytes read/wrote

DWORD fIsReadif 1, then read pci config

if 0, then write pci config

DWORD dwResultPCMCIA_ACCESS_RESULT

8.25  WD_USB_ID

Defines the identity of the USB device.

MEMBERS:

TYPE NAME DESCRIPTION
DWORDdwVendorIdVendor ID of the USB device
DWORDdwProductIdproduct ID of the USB device

8.26  WD_USB_PIPE_INFO

Information about a pipe.

MEMBERS

TYPE NAME DESCRIPTION
DWORDdwNumberThe number of the pipe (Pipe 0 is the default pipe)
DWORDdwMaximum PacketSizethe maximum packet size of internal transfers on the pipe
DWORDtypeControl, Isochronous, Bulk or Interrupt
DWORDdirectionIn=1, out=2 or in&out=3
DWORDdwIntervalIntervals of data transfer in ms (relevant to Interrupt pipes)

8.27  WD_USB_CONFIG_DESC

Describes a configuration.

MEMBERS

TYPE NAME DESCRIPTION
DWORDdwNumInter facesthe configuration number
DWORDdwValuethe device value
DWORDdwAttributesthe device attributes
DWORD Maxpowerthe device MaxPower

8.28  WD_USB_INTERFACE_DESC

Describes an interface.

MEMBERS

TYPE NAME DESCRIPTION
DWORDdwNumberthe interface number
DWORD dwAlternate Settingthe interface alternate value
DWORDdwNumEnd pointsthe number of endpoints in the interface
DWORDdwClassthe interface class
DWORDdwSubClassthe interface sub class
DWORDdwProtocolthe interface protocol
DWORDdwIndexthe index of the interface

8.29  WD_USB_ENDPOINT_DESC

Describes an endpoint.

MEMBERS

TYPE NAME DESCRIPTION
DWORDdwEndpoint Addressend point address
DWORDdwAttributesend point attributes
DWORDdwMaxPacket Sizemaximum packet size
DWORDdwIntervalinterval in milli-seconds

8.30  WD_USB_INTERFACE

Holds interface data.

MEMBERS

TYPE NAME DESCRIPTION
WD_USB_INTER FACE_DESC [8.28]Interfacethe interface description
WD_USB_END POINT_DESC [8.29]Endpoints[]list of the interface endpoints

8.31  WD_USB_CONFIGURATION

Holds configuration data.

MEMBERS

TYPE NAME DESCRIPTION
DWORDuniqueIdthe unique ID of the device
DWORDdwConfiguration Indexthe Configuration Index
WD_USB_CONFIG_ DESC [8.27]configurationthe configuration description
DWORDdwInterfaceAl ternativesnumber of interfaces and their alternatives.
WD_USB_INTER FACE [8.30]Interface[]list of configuration interfaces

8.32  WD_USB_HUB_GENERAL_INFO

Holds hub information (if the selected device is a hub).

MEMBERS

TYPE NAME DESCRIPTION
DWORDfBusPoweredis bus powered or self powered
DWORDdwPortsnumber of ports on this hub
DWORDdwCharacter isticshub characteristics
DWORDdwPowerOn ToPowerGoodport power on till power good in ms
DWORDdwHubControl Currentmax current in mA

8.33  WD_USB_DEVICE_GENERAL_INFO

General information about the device.

MEMBERS

TYPE NAME DESCRIPTION
WD_USB_ID [8.25]deviceIdthe vendor ID and product ID of the device
DWORDdwHubNumthe number of the hub to which the device is attached
DWORDdwPortNumthe number of the port on the hub to which the device is attached
DWORDfHubis the device itself a hub?
DWORDfFullSpeedfull speed or low speed device?
DWORDdwConfigurations Numhow many configurations does the device have?
DWORDdeviceAddressthe physical address of the device
WD_USB_ HUB_GENERAL_ INFO [8.32]hubInfocontains information about the device, if the device is a Hub

8.34  WD_USB_DEVICE_INFO

Holds device pipes information.

MEMBERS

TYPE NAME DESCRIPTION
DWORDdwPipesnumber of pipes
WD_USB_ PIPE_INFO [8.26]Pipe[]the list of pipes information

8.35  WD_USB_SCAN_DEVICES

Define a scan command.

MEMBERS

TYPE NAME DESCRIPTION
WD_USB_ ID [8.25]searchIdif dwvendorId ==0, then scan all vendor IDs.

if dwProductId ==0, then scan all products.

DWORDdwDevicesNumber of devices found
DWORDuniqueId[]a list of the uniqueIDs to identify the devices
WD_USB_ DEVICE_ GENERAL_ [8.33]deviceGeneral Info[]list of general information about the devices found

8.36  WD_USB_TRANSFER

Defines a transfer command.

MEMBERS

TYPE NAME DESCRIPTION
DWORDhDevicehandle of USB device to read from or write to
DWORDdwPipepipe number on device
DWORDfReadread or write
DWORDdwOptionscan be USB_TRANSFER_HALT to halt the previous transfer on the same pipe
DWORDpBufferpointer to buffer to read / write
DWORDdwBytesthe size of the buffer
DWORDdwTimeouttransfer timeout(milliseconds) 0==> no timeout
DWORDdwBytes Transferedreturns the number of bytes actually read / written
BYTESetupPacket[8]setup packet for control pipe transfer
DWORDfOKreturn TRUE if the transfer is successful

8.37  WD_USB_DEVICE_REGISTER

Define a device registration command.

MEMBERS

TYPE NAME DESCRIPTION
DWORDuniqueIdthe unique ID of the device
DWORDdwConfiguration Indexthe index of the configuration to register
DWORDdwInterfaceNuminterface to register
DworddwInterfaceAl ternatealternate number of the interface to register
DWORDhDevicehandle of the device
WD_USB_ DEVICE_ INFO [8.34]Devicedescription of the device
DWORDdwOptionsshould be zero
CHARcName[32]name of card
CHAR cDescription [100]description

8.38  WD_USB_RESET_PIPE

Defines a reset pipe command.

MEMBERS

TYPE NAME DESCRIPTION
DWORDhDevicehandle of the device
DWORDdwPipenumber of the pipe to reset

Chapter 9
WinDriver Enhanced Support for Specific PCI Chip Sets

This chapter is relevant to you if you are using one of the PCI chip-sets for which WinDriver offers Enhanced support. This currently includes PLX 9030, 9050, 9052, 9054, 9060, 9080, IOP 480, Galileo gt64, IOP480, Altera, V3 PBC and AMCC 5933. WinDriver supports all other PCI chip-sets via DriverWizard and the regular WinDriver API.

9.1  Overview

In addition to the regular WinDriver API, described in the earlier chapters, WinDriver also offers a custom API for specific PCI chip-sets ¡ Currently PLX, Galileo, V3, Altera, PLDA, QuickLogic and AMCC chip-sets.

The following is an overview of the development process when using WinDriver specific PCI API:

  1. Run the custom diagnostics program to diagnose your card.

  2. Locate your specific card diagnostics program. See \WinDriver\chip_vendor \chip_name\xxxdiag\xxxdiag.c

  3. Use this source code as your skeletal device driver.

  4. Modify the code to suit your application. Use your PCI chip specific function reference to add your own code. More help and details can be found in the WinDriver electronic reference manual.

  5. If the User Mode driver you have created in the above steps contains some parts which requires enchanced performance (an interrupt handler for example), see Chapter 12 that explains the WinDriver Kernel PlugIn. There you learn how to move parts of your source code to WinDriver's Kernel PlugIn, thereby eliminating any calling overhead, and achieving maximal performance.

9.2  What is the PCI Diagnostics program?

The diagnostics program is a ready-to-run sample diagnostics application for specific PCI chip-sets. The diagnostics program accesses the hardware via WinDriver's specific PCI API (xxxLIB.C). It is written as a console mode application, and not as a GUI application, to simplify the understanding of it's source code of the diagnostics program. This will help you learn how to properly use the specific API.

This application can be used as your skeletal device driver. If your driver is not a console mode application, just remove the printf() calls from the code (you may replace them with MessageBox() if you wish).

You may find that xxx_DIAG.C is both an example of using your specific API as well as a useful diagnostics utility.

9.3  Using your PCI chip-set Diagnostics program

9.3.1  Introduction

The custom diagnostics program (xxx_DIAG.EXE) accesses the hardware using WinDriver. Therefore WinDriver must be installed before xxx_DIAG is run. If WinDriver is installed correctly, a message will appear on screen at boot time displaying the WinDriver version installed.

Once WinDriver is running, you may run xxx_DIAG by clicking on Start | Programs | WinDriver | Samples | Chip_name Diagnostics.

The application will first try to locate the card, with the default VendorID and DeviceID assigned by your PCI chip vendor (for example ¡ PLX 9054 - VendorID = 0x10b5, DeviceID = 0x9054). If such a card is found you will get a message ``your PCI card found'' (``PLX 9054 card found''). If you have programmed your EEPROM to load a different VendorID/DeviceID, then at the main menu you will have to choose your card (option `Locate/Choose your board' in main menu).

9.3.2  Main Menu Options

9.3.2.1  Scan PCI bus

Displays all the cards present on the PCI bus and their resources. (IO ranges, Memory ranges, Interrupts, VendorID / DeviceID). This information may be used to choose the card you need to access.

9.3.2.2  Locate / Choose your board

Chooses the active card that the diagnostics application will use. You are asked to enter the VendorID/DeviceID of the card you want to access. In case there are several cards with the same VendorID/DeviceID, you will be asked to choose one of them.

9.3.2.3  PCI configuration registers

This option is available only after choosing an active card. A list of the PCI configuration registers and their READ values are displayed. These are general registers, common to all PCI cards. In order to WRITE to a register, enter its number, and then the value to write to it.

9.3.2.4  Your PCI local registers

This option is available only after choosing an active card. A list of your PCI registers and their READ values are displayed. In order to WRITE to a register, enter the register number, and then enter the value to write to it.

9.3.2.5  Access memory ranges on the board

This option is available only after choosing an active card. Use this option carefully. Accessing memory ranges, accesses the local bus on your card - If you access an invalid local address, or if you have any problem with your card (such as a problem with the IRDY signal), the CPU may hang.

Both in board READ and WRITE, the address you give will also be used to set the base address register. For More detail see the electronic reference manual.

9.3.2.6  Enable / Disable Interrupts

This option will appear only if the card was set to open with interrupts. Choosing this item toggles the interrupt status (Enable / Disable). When interrupts are disabled, interrupts that the card generates are not intercepted by the application. If interrupts are generated by the hardware while the interrupts are disabled by the application, the computer may `hang'.

9.3.2.7  Access EEPROM device (Where available)

This option provides basic read/write access to the serial configuration EEPROM. This is available only after choosing an active card. This option assumes that the configuration EEPROM has initialized the Configuration Register, Aperture zero and one space to valid local.

  1. To read an EEPROM location, choose `Read a byte from serial EEPROM'. You will be asked for the address of the location to read from.
  2. To write an EEPROM location, choose `Write a byte to serial EEPROM'. You will be asked for the address and the data to write.

9.3.2.8  Pulse Local Reset (where available)

This option provides a way to reset the local processor from the host.

To RESET the local host processor, choose `Enter reset duration in milliseconds'. You will be asked for the time in milliseconds.

Note: Resolution of delay time is based on PC timer tick, or approximately 55 milliseconds.

9.4  Creating your driver without using the PCI diagnostics code

NOTE: In your \windriver\chip_vendor
\chip_name\xxx_diag
folder, you will find the source code for xxx_DIAG.EXE. Double click the `mdp' file (which contains the project environment used to compile this code) in this directory to start your MSDEV with the proper settings for a project. You may use this as your skeletal code.

  1. xxx_IsAddrSpaceActive()

  2. xxx_GetRevision()

  3. xxx_ReadReg ()

  4. xxx_WriteReg ()

  5. xxx_ReadSpaceBlock()

  6. xxx_WriteSpaceBlock()

  7. xxx_ReadSpaceByte()

  8. xxx_ReadSpaceWord()

  9. xxx_ReadSpaceDWord()

  10. xxx_WriteSpaceByte()

  11. xxx_WriteSpaceWord()

  12. xxx_WriteSpaceDWord()

  13. xxx_ReadBlock()

  14. xxx_WriteBlock()

  15. xxx_ReadByte()

  16. xxx_ReadWord()

  17. xxx_ReadDWord()

  18. xxx_WriteByte()

  19. xxx_WriteWord()

  20. xxx_WriteDWord()

  21. xxx_IntIsEnabled()

  22. xxx_IntEnable()

  23. xxx_IntDisable()

  24. xxx_DMAOpen()

  25. xxx_DMAClose()

  26. xxx_DMAStart()

  27. xxx_DMAIsDone()

  28. xxx_EEPROMRead()

  29. xxx_EEPROMWrite()

  30. xxx_ReadPCIReg()

  31. xxx_WritePCIReg()

NOTES

  1. Using one of the sample drivers included with WinDriver as your skeletal code may shorten the development process.
  2. APIs may slightly vary between PCI chips. See the electronic manual (Start Menu | Programs | WinDriver | WinDriver Manual) for details.

Sample Code

Sample uses of WinDriver for all PCI chip sets are supplied with the WinDriver toolkit.

You may find the WinDriver samples under \windriver\samples, and the WinDriver for PLX/Galileo/V3/AMCC samples under \windriver\chip_vendor. Each directory contains files.txt, which describes the various samples included.

Each sample is located in its own directory. For your convenience, we have supplied an `mdp' file alongside each `.c' file, so that users of Microsoft's Developers Studio may double-click the mdp file and have the whole environment ready for compilation. (Users of other win32 compilers need to include the *.c files in their stand-alone console project, and include the xxx_lib.c in their project)

You may use the source of the diagnostic program described earlier to learn your PCI's specific API usage.

9.5  WinDriver's specific PCI chip-set API Function Reference

Use this section as a `quick reference' to WinDriver's specific PCI API functions. A more detailed reference (per chip) may be found in the WinDriver's Help files.

Advanced users may find more functionality in WinDriver's API.

All the functions outlined in Chapter 7 that details the WinDriver function reference are implemented in the respective \WinDriver\chip_vendor\chip_name\lib\xxx_lib.c file.

For more detailed information on specific PCI chip set APIs see the electronic reference manual.

The definition of the structures used in the following functions are found in the Electronic Reference Manual

9.5.1  xxx_CountCards ()

Returns the number of cards on the PCI bus that have the given VendorID and DeviceID.

This value can then be used when calling xxx_Open, to select which board to open. Normally, only one board is in the bus and this function will return 1.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Returns the number of matching PCI cards found.

EXAMPLE

nCards = P9054_CountCards( 0x10b5, 0x9054 ); 

9.6  xxx_Open()

Used to open a handle to your card. If several cards with identical PCI chips are installed, the specific card to open may be specified by using `xxx_CountCards' before using `xxx_Open', and then calling `open' with a specific card number.

If Open is successful, the function returns TRUE, and a handle to the card.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

TRUE if OK.

EXAMPLE

if (!P9054_Open( &hPlx, 0x10b5, 0x9054, 0, P9054_OPEN_USE_INT )) 

{ 

  printf("Error opening device\n"); 

} 

9.7  xxx_Close()

Closes WinDriver device. Must be called after finished using the driver.

PROTOTYPE AND PARAMETERS - See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

EXAMPLE

P9054_Close(hPLX); 

9.8  xxx_IsAddrSpaceActive()

Checks if the specified address space is enabled. The enabled address spaces are determined by the EEPROM, which at boot time sets the memory ranges requests.

Use this function after calling xxx_Open() to make sure that the address space(s) that your driver is going to use are enabled.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

TRUE if address space is enabled

EXAMPLE

if ( !P9054_IsAddrSpaceActive(hPlx, P9054_ADDR_SPACE2) ) 

{ 

  printf ("Address space2 is not active!\n"); 

} 

9.9  xxx_GetRevision()

Returns your PCI chip-set silicon revision.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Returns the silicon revision.

9.10  xxx_ReadReg ()

Reads data from a specified register on the board.

PROTOTYPE AND PARAMETERS - See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from register (for P9054_ReadReg() only).

9.11  xxx_WriteReg ()

Writes data to a specified register on the board.

PROTOTYPE AND PARAMETERS - See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.12  xxx_ReadSpaceByte()

Reads a byte from address space on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from board.

9.13  xxx_ReadSpaceWord()

Reads a word from address space on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from board.

9.14   xxx_ReadSpaceDWord()

Reads a dword from address space on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from board.

9.15   xxx_WriteSpaceByte()

Writes a byte from address space on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.16   xxx_WriteSpaceWord()

Writes a word from address space on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.17   xxx_WriteSpaceDWord()

Writes a dword from address space on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.18   xxx_ReadSpaceBlock()

Reads a block from address space on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from the board

9.19   xxx_WriteSpaceBlock()

Writes a block from address space on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.20   xxx_ReadByte()

Reads a byte from memory on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from board.

9.21   xxx_ReadWord()

Reads a word from memory on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from board.

9.22   xxx_ReadDWord()

Reads a dword from memory on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from board.

9.23   xxx_WriteByte()

Writes a byte to memory on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.24   xxx_WriteWord()

Writes a word to memory on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None.

9.25   xxx_WriteDWord()

Writes a dword to memory on board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None.

9.26   xxx_ReadBlock()

Reads a block of memory from the board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from the board

9.27   xxx_WriteBlock()

Writes a block of memory to the board.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.28   xxx_IntIsEnabled()

Checks whether interrupts are enabled or not.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

TRUE if interrupts are already enabled (e.g. if P9054_IntEnable() was called).

9.29   xxx_IntEnable()

Enable interrupt processing.

IMPORTANT NOTE: All PCI chip-sets use level sensitive interrupts. Hence, you must edit the implementation of this function (found in your

\WinDriver\chip_vendor\chip_name \lib\xxx_lib.c) to fit your specific hardware.

The comments in this function indicate the places where changes must be inserted. PROTOTYPE AND PARAMETERS

See the electronic reference manual for the PCI chip specific details.

RETURN VALUE

TRUE if successful.

9.30   xxx_IntDisable()

Disable interrupt processing.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.31   xxx_DMAOpen()

Initializes the WD_DMA structure (see windrvr.h) and allocates a contiguous buffer

WD_DMA stucture

typedef struct { 
  DWORD hDma;      // handle of dma buffer 
  PVOID pUserAddr; // beginning of buffer 
  DWORD dwBytes;   // size of buffer 
  DWORD dwOptions; // allocation options: 

                   // DMA_KERNEL_BUFFER_ALLOC, 
                   // DMA_KBUF_BELOW_16M, 
                   // DMA_LARGE_BUFFER 

  DWORD dwPages;   // number of pages in buffer 

  WD_DMA_PAGE Page[WD_DMA_PAGES]; 

} WD_DMA, WD_DMA_V30;

The definition of the structure WD_DMA_PAGE is as follows:

typedef struct { 
  PVOID pPhysicalAddr; // physical address of page 
  DWORD dwBytes;       // size of page 
} WD_DMA_PAGE, WD_DMA_PAGE_V30; 

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Returns TRUE if DMA buffer allocation succeeds

9.32   xxx_DMAClose()

Frees the DMA handle, and frees the allocated contiguous buffer.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.33   xxx_DMAStart()

Start DMA to or from the card.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Returns TRUE if DMA transfer succeeds.

9.34   xxx_IsDMADone()

Used to test if DMA is done. (Use when V3PBC_DMAStart is called with fBlocking == FALSE)

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Returns TRUE if DMA transfer is completed.

9.35   xxx_PulseLocalReset()

Sends a reset signal to the card, for a period of `wDelay' milliseconds.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

9.36   xxx_EEPROMRead()

Reads data from the EEPROM. - Syntax and functionality may vary between different chip-sets. See the electronic reference manuals for your chip-set's exact syntax and usage.

PROTOTYPE AND PARAMETERS

See the electronic reference manual for your PCI chip specific details

RETURN VALUE

Returns the data read.

9.37   xxx_EEPROMWrite()

Writes data to the EEPROM. - Syntax and functionality may vary between different chip-sets. See the electronic reference manuals for your chip-set's exact syntax and usage.

PROTOTYPE AND PARAMETERS

See the electronic reference manual for your PCI chip specific details

RETURN VALUE

Returns TRUE if EEPROM write succeeds.

9.38   xxx_ReadPCIReg ()

Read data from the PCI configuration registers.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

Data read from configuration register

9.39   xxx_WritePCIReg()

Write to the PCI configuration registers.

PROTOTYPE AND PARAMETERS

See electronic reference manual for your PCI chip specific details.

RETURN VALUE

None

Structure Reference for WinDriver's specific PCI APIs

Structure reference for the PLX, Galileo, Altera, V3, and AMCC specific APIs can be found in the electronic version of the manual.

Chapter 10
WinDriver Implementation Issues

This chapter contains instructions for performing operations that DriverWizard cannot automate. If you are using a PCI chip set from PLX, Galileo, Altera, AMCC, or V3 ¡ you do not have to read this chapter.

WinDriver includes custom APIs built especially for these PCI chip-set vendors. These APIs save you the need to learn both the PCI internals and the chip-set's data sheets. Using these specific APIs ¡ a DMA function is as simple as calling a function (i.e. P9054_DMAOpen(), P9054_DMAStart() and so on... ).

10.1  Performing DMA

If you are not using a PCI chip which has enhanced support WinDriver (currently - PLX, Galileo, Altera, V3 or AMCC) ¡ these few pages will guide you through the steps of performing DMA via WinDriver's API.

There are basically two methods to perform DMA - Contiguous Buffer DMA and Scatter/Gather DMA. Scatter/Gather DMA is much more efficient than contiguous DMA. This feature allows the PCI device to copy memory blocks from different addresses. This means that the transfer can be done directly to/from the user's buffer - that is contiguous in Virtual memory, but fragmented in the Physical memory. If your PCI device does not support Scatter/Gather, you will need to allocate a physically contiguous memory block, perform the DMA transfer to there, and then copy the data to your own buffer.

The programming of DMA is specific for different PCI devices. Normally, you need to program your PCI device with the Local address (on your PCI device), the Host address (the physical memory address on your PC), the transfer count (size of block to transfer), and then set the register that initiates the transfer.

10.1.1  Scatter/Gather DMA

Following is an outline of a DMA transfer routine for PCI devices that support Scatter/Gather DMA. More detailed examples can be found at:

Note for Linux developers: Due to Linux's own limitations WinDriver does not yet support Scatter Gather DMA on this OS. This feature will be added to the Linux version of WinDriver as soon as the Linux kernel includes support for Scatter Gather DMA operations.

Sample DMA implementation:

BOOL DMA_routine(void *startAddress, DWORD transferCount, 
 BOOL fDirection)

{

  WD_DMA dma;

  int i;

  BZERO (dma);

  dma.pUserAddr = startAddress;

  dma.dwBytes = transferCount;

  dma.dwOptions = 0;

  // lock region in memory
  WD_DMALock(hWD,&dma);
  if (dma.hDma==0)
    return FALSE;
  for(i=0;i!=dma.dwPages;i++)
  {
    // Program the registers for each page of the transfer
    My_DMA_Program_Page(dma.Page[i].pPhysicalAddr,
     dma.Page[i].dwBytes, fDir);
  }
  // write to the register that initiates the DMA transfer
  My_DMA_Initiate();
    // read register that tells when the DMA is done
  while(!My_DMA_Done());
  WD_DMAUnlock(hWD,&dma);
  return TRUE;

}

You should implement:

10.1.1.1  Scatter/Gather DMA for buffers larger than 1MB

The WD_DMA structure holds a list of 256 pages. The x86 CPU uses 4K page size, so 256 pages can hold 256*4K = 1MB. Since the first and last page might not start (or end) on a 4096 byte boundary, 256 pages can hold 1MB - 8K.

If you need to lock down a buffer larger than 1MB, that needs more than 256 pages, you will need the DMA_LARGE_BUFFER option.

BOOL DMA_Large_routine(void *startAddress, DWORD transferCount,
    BOOL fDirection)
{
  DWORD dwPagesNeeded = transferCount / 4096 + 2;
  WD_DMA *dma=calloc
   (sizeof(WD_DMA)+sizeof(WD_DMA_PAGE)*dwPagesNeeded,1);
  dma->pUserAddr = startAddress;
  dma->dwBytes = transferCount;
  dma->dwOptions = DMA_LARGE_BUFFER;
  dma->dwPages = dwPagesNeeded;
  // lock region in memory
  WD_DMALock(hWD,&dma);
  // the rest is the same as in the DMA_routine()
  // free the WD_DMA structure allocated
  free (dma);
}

10.1.2  Contiguous Buffer DMA

More detailed examples can be found at:

A read sequence (from the card to the mother-board's memory):

{ 
  WD_DMA dma;
  BZERO (dma);
  // allocate the DMA buffer (100000 bytes)
  dma.pUserAddr = NULL;
  dma.dwBytes = 10000;
  dma.dwOptions = DMA_KERNEL_BUFFER_ALLOC;
  WD_DMALock(hWD, &dma);
  if (dma.hDma==0)
    return FALSE;
  // transfer data from the card to the buffer
  My_Program_DMA_Transfer(dma.Page[0].pPhysicalAddr, 
  // Wait for transfer to end
  while(!My_Dma_Done());
  // now the data is the buffer, and can be used
  UseDataReadFromCard(dma.pUserAddr);
  // release the buffer
  WD_DMAUnlock(hWD,&dma);
}

A Write Sequence (from the mother-board's memory to the card):

{

  WD_DMA dma;

  BZERO (dma);

  //allocate the DMA buffer (100000 bytes)

  dma.pUserAddr = NULL;

  dma.dwBytes = 10000;

  dma.dwOptions = DMA_KERNEL_BUFFER_ALLOC;

  WD_DMALock(hWD, &dma);

  if (dma.hDma==0)

    return FALSE;

  // prepare data into buffer

  PrepareDataInBuffer(dma.pUserAddr);

  // transfer data from the buffer to the card

  My_Program_DMA_Transfer(dma.Page[0].pPhysicalAddr, 
   LocalAddr);

  // Wait for transfer to end

  while(!My_Dma_Done());

  // release the buffer

  WD_DMAUnlock(hWD,&dma);

}

10.2  Handling Interrupts

Interrupts can easily be handled via DriverWizard. It is recommended that you use DriverWizard to generate the interrupt code for you, by defining (or auto-detecting) your hardware's interrupts, and generating code. Use this section to understand the code DriverWizard generates for you or to write your own Interrupt handler.

10.2.1  General - Handling an Interrupt

The WD_IntWait() function, puts the thread to sleep until an interrupt occurs. There is no CPU consumption while waiting for an interrupt. Once an interrupt occurs, it is first handled by the WinDriver kernel, then the WD_IntWait() wakes up the interrupt handler thread and returns.

Since your interrupt thread runs in user-mode, you may call any Windows API function, including File handling and GDI functions.

Simple interrupt handler routine, for edge-triggered interrupts (normally ISA/EISA cards):

// interrupt structure

WD_INTERRUPT Intrp;

DWORD WINAPI wait_interrupt (PVOID pData)

{

  printf ("Waiting for interrupt");
  for (;;)

  {

    WD_IntWait (hWD, &Intrp);
    if (Intrp.fStopped)

      break; // WD_IntDisable called by parent
  // call your interrupt routine here
      printf ("Got interrupt %d\n", Intrp.dwCounter);

  }
  return 0;
}

void Install_interrupt()
{
  BZERO(Intrp);
  // put interrupt handle returned by WD_CardRegister
  Intrp.hInterrupt = cardReg.Card.Item[0].I.Int.hInterrupt;
  // no kernel transfer commands to do upon interrupt
  Intrp.Cmd = NULL;
  Intrp.dwCmds = 0;
  // no special interrupt options
  Intrp.dwOptions = 0;
  WD_IntEnable(hWD, &Intrp);
  if (!Intrp.fEnableOk)
  {
    printf ("Failed enabling interrupt\n");
    return;
  }

  printf ("starting interrupt thread\n");

  thread_handle = CreateThread (0, 0x1000, 
    wait_interrupt, NULL, 0, &thread_id);

  // call your driver code here

  WD_IntDisable (hWD, &Intrp);
  WaitForSingleObject(thread_handle, INFINITE);

}

10.2.2  Simplified interrupt handling using windrvr_int_thread.h

From Version 4.3 onwards, a new header file windrvr_int_thread.h simplifies the code you need to write to handle interrupts. In this header file - found under windriver/include, we provide the convienence functions InterruptThreadEnable [7.30] and InterruptThreadDisable [7.31]. These functions are implemented as static functions in the header file windrvr_int_thread.h. Please study the code in the header file to understand how this operates. We may rewrite the code from Section 10.2.1 as follows. This code was extracted from the sample program int_io.c which can be found under windriver/samples/int_io. Please refer to this file for the full listing.

// interrupt handler routine. you can use pData to pass 
// information from InterruptThreadEnable()
VOID interrupt_handler (PVOID pData)
{
    WD_INTERRUPT * pIntrp = (WD_INTERRUPT *) pData;
    // do your interrupt routine here 
    printf ("Got interrupt %d\n", pIntrp->dwCounter);
}

...

int main()
{
    HANDLE hWD;
    WD_CARD_REGISTER cardReg;
    // interrupt structure
    WD_INTERRUPT Intrp;
    HANDLE thread_handle;
    
    ...
    hWD = WD_Open();
    BZERO(cardReg);
    cardReg.Card.dwItems = 1;
    cardReg.Card.Item[0].item = ITEM_INTERRUPT;
    cardReg.Card.Item[0].fNotSharable = TRUE;
    cardReg.Card.Item[0].I.Int.dwInterrupt = MY_IRQ;
    cardReg.Card.Item[0].I.Int.dwOptions = 0;
    ...
    WD_CardRegister (hWD, &cardReg);
    ...
    PVOID  pData = NULL;
    BZERO(Intrp);
    Intrp.hInterrupt = cardReg.Card.Item[0].I.Int.hInterrupt;
    Intrp.Cmd = NULL;
    Intrp.dwCmds = 0;
    Intrp.dwOptions = 0;
    printf ("starting interrupt thread\n");
    // this calls WD_IntEnable() and creates an interrupt 
    // handler thread which calls the function 
    // interrupt_handler with the pointer pData as a parameter
 
    pData = &Intrp;
    ...
    if (!InterruptThreadEnable(&thread_handle, hWD, &Intrp, 
         interrupt_handler, pData))
      {
         printf ("failed enabling interrupt\n");
      }
    else
      {
        // call your driver code here 
        printf ("Press Enter to uninstall interrupt\n");
        fgets(line, sizeof(line), stdin);

        // this calls WD_IntDisable()
        InterruptThreadDisable(thread_handle);
      }
    WD_CardUnregister(hWD, &cardReg);
    ....
}

In the above code, the function interrupt_handler serves as our ``interrupt handler'', getting invoked once for every interrupt that occurs. In the simplified code for setting up the interrupt handling, we call InterruptThreadEnable[7.30] spawn a thread that calls the function interrupt_handler - a pointer to this function is passed as the fourth parameter to InterruptThreadEnable - each time an interrupt occurs, passing into this function, the data pData specified by the fifth parameter.

10.2.3  ISA / EISA and PCI interrupts

Generally, ISA/EISA interrupts are edge triggered, as opposed to PCI interrupts that are level sensitive. This difference has many implications on writing the interrupt handler routine.

Edge triggered interrupts are generated once, when the physical interrupt signal goes from low to high. Therefore, exactly one interrupt is generated. This makes the Windows OS to call the WinDriver kernel interrupt handler, that released the thread waiting on the WD_IntWait() function. There is no special action that needs to take place in order to acknowledge this interrupt.

Level sensitive interrupts are generated as long as the physical interrupt signal is high. If the interrupt signal is not lowered by the end of the interrupt handling by the kernel, the Windows OS will call the WinDriver kernel interrupt handler again - This will cause the PC to hang!

To prevent this situation from happening, the interrupt must be acknowledged by the WinDriver kernel interrupt handler. Explanation on acknowledging level-sensitive interrupts can be found under Section 19.6 that explains how to deal with the problem of the computer hanging on interrupt.

Transfer commands at kernel-level (acknowledging the interrupt)

Usually, interrupt handlers for PCI cards (level sensitive interrupt handlers) need to perform transfer commands at the kernel to lower the interrupt level (acknowledge the interrupt).

To pass transfer commands to be performed in the WinDriver kernel interrupt handler, before WD_IntWait() returns, you must prepare an array of commands
(WD_Transfer structure), and pass it to the WD_IntEnable() function.

WD_TRANSFER trans[2];
BZERO(trans);
trans[0].cmdTrans = RP_DWORD; // Read Port Dword
// address of IO port to write to
trans[0].dwPort = dwAddr;
trans[1].cmdTrans = WP_DWORD; // Write Port Dword
// address of IO port to write to
trans[1].dwPort = dwAddr;
// the data to write to the IO port
trans[1].Data.Dword = 0;
Intrp.dwCmds = 2;
Intrp.Cmd = trans; 
Intrp.dwOptions =
      INTERRUPT_LEVEL_SENSITIVE | INTERRUPT_COPY_CMD;
WD_IntEnable(hWD, &Intrp);

This sample performs a DWORD read command from the IO address dwAddr, then it writes to the same IO port a value of `0'.

The INTERRUPT_COPY_CMD option is used to retrieve the value read by the first transfer command, before the write command is issued. This is useful when you need to read the value of a register, and then write to it to lower the interrupt level. If you try to read this register after WD_IntWait() returns, it will already be `0' because the write transfer command was issued at kernel level.

DWORD WINAPI wait_interrupt (PVOID pData)
{
   printf ("Waiting for interrupt");
   for (;;)
   {
     WD_TRANSFER trans[2];
     Intrp.dwCmds = 2;
     Intrp.Cmd = trans;
     WD_IntWait (hWD, &Intrp);
     if (Intrp.fStopped)

        break; // WD_IntDisable called by parent
               // call your interrupt routine here
        
        printf (
                "Got interrupt %d. Value of register read %x\n",
               Intrp.dwCounter, trans[0].Data.Dword);
}
return 0;
}

If you study the implementation of the interrupt handling in windrvr_int_thread.h, you see that code similar to the above is used there.

10.2.4  Interrupts in Windows CE

Windows CE uses a logical interrupt scheme rather than the physical interrupt number. It maintains an internal kernel table that maps the physical IRQ number to the logical IRQ number. Device drivers are generally expected to get the logical interrupt number after having ascertained the physical interrupt.

This is handled internally by WinDriver so programmers using WinDriver need not worry about this issue. However, the X86 CEPC builds provided with the ETK do not provide interrupt mappings for certain reserved interrupts including the following:

An attempt to initialize and use any of these interrupts will fail. In case you wish to use any of these interrupts - (e.g.: you do not want to use the PPSH and you want to reclaim the parallel port for some other purpose) - you should modify the file CFWPC.C that is found in the directory %_TARGETPLATROOT%\KERNEL\HAL to include code as shown below that sets up a value for the interrupt 7 in the interrupt mapping table.

SETUP_INTERRUPT_MAP(SYSINTR_FIRMWARE+7,7);

Supposing you have a PCI card in your X86 CEPC and the BIOS assigned IRQ9 to it. Since WinCE does not map this interrupt by default, you will not be able to receive interrupts from this card. In this case, you will need to insert a similar entry for IRQ 9.

SETUP_INTERRUPT_MAP(SYSINTR_FIRMWARE+9,9);

You will then need to rebuild the Windows CE image NK.BIN and download the new executable onto your target platform.

For non-X86 machines like the hand-held PCs from HP and Sharp, the developer should use the logical interrupt ID which can be in the platform specific header file NKINTR.H

A complete discussion of this procedure is outside the scope of this manual. Please refer to the ETK or Platform Builder documentation for more details.

10.2.5  PCMCIA interrupts in Windows CE

Windows CE handles PCMCIA interrupts differently than PCI and ISA/EISA interrupts. WinDriver handles the setting up of the PCMCIA interrupt internally by calling the Card Services API so this process is transparent to the developer.
The WD_PcmciaGetCardInfo() function automatically sets up the interrupt items and registers the interrupt.

10.3  USB Control Transfers

10.3.1  USB Data Exchange

The USB standard supports two kinds of data exchange between the host and the device: functional data exchange and control exchange.

The control transfer consists of a setup stage (in which a setup packet is sent from the host to the device), an optional data stage and a status stage.

images/usb_data_exchange.png

Figure 10.1: USB Data Exchange

10.3.2  More about the Control Transfer

The control transaction always begins with a setup stage. Then, it is followed by zero or more control data transactions (data stage) that carry the specific information for the requested operation, and finally, a Status transaction completes the control transfer by returning the status to the host.

During the setup stage, a SETUP Packet is used to transmit information to the control endpoint of the device. The Setup Packet consists of eight bytes, and its format is specified in the USB specification.

A control transfer can be a read transaction or a write transaction. In a read transaction, the Setup Packet indicates the characteristics and amount of data to be read from the device. In a write transaction, the Setup Packet contains the command sent (written) to the device and the number of control Data bytes, associated with this transaction, that are sent to the device in the data stage.

The following figure shows the sequence of read and write transactions (the figure is taken from the USB specification). `In' means the data flows from the device to the host. `Out' means the data flows from the host to the device.

images/usb_read_write.png

Figure 10.2: USB Read and Write

10.3.3  The Setup Packet

The Setup Packets (combined with the control data stage and the status stage) are used to configure and send commands to the device. Chapter 9 of the USB specification defines standard device requests. USB requests such as these are sent from the host to the device, using Setup Packets. The USB device is required to respond properly to these requests. In addition, each vendor may define device-specific Setup Packets, to perform device specific operations. The standard Setup Packets (standard USB device requests) are detailed below. The vendor's device-specific Setup Packets are detailed in the vendor's specific data book for each USB device.

10.3.4  USB Setup Packet Format

The table below shows the format of the USB setup packet. (For further information please refer to the USB specification at http://www.usb.org.)

ByteFieldDescription
0bmRequest TypeBit 7: Request direction (0=Host to device - out, 1=Device to host - in)

Bits 5..6:Request type (0=standard, 1=class, 2=vendor, 3=reserved)

Bits 0..4:Recipient (0=device, 1=interface, 2=endpoint,3=other)

1bRequestThe actual request (see next table)
2wValueLA word-size value that varies according to the request (for example in the CLEAR_FEATURE request, the value is used to select the feature, in the GET_DESCRIPTOR request, the value indicates the descriptor type, in the SET_ADDRESS request, the value contains the device address)
3wValueHThe upper byte of the Value word.
4wIndexLA word size value that varies according to the request. The index is generally used to specify an endpoint or an interface.
5wIndexHThe upper byte of the Index word.
6wLengthLWord size value, indicates the number of bytes to be transferred if there is a data stage.
7wLengthHThe upper byte of the Length word.

10.3.5  Standard device requests codes

bRequestValue
GET_STATUS0
CLEAR_FEATURE1
Reserved for future use2
SET_FEATURE3
Reserved for future use4
SET_ADDRESS5
GET_DESCRIPTOR6
SET_DESCRIPTOR7
GET_CONFIGURATION8
SET_CONFIGURATION9
GET_INTERFACE10
SET_INTERFACE11
SYNCH_FRAME12

10.3.6  Setup Packet example

This is an example of a standard USB device request to illustrate the Setup Packet format and its different fields. The Setup packet is in Hex format.

The following Setup Packet is a `Control Read' transaction that retrieves the `Device descriptor' from the USB device. The `Device descriptor' includes information such as USB standard revision, the vendor ID and the device product ID.

GET_DESCRIPTOR (device) Setup Packet

8006000100001200

Setup Packet meaning:

ByteFieldValueDescription
0BmRequest Type808h=1000b

bit 7=1 -> direction of data is from device to host.

0h=0000b

bits 0..1=00 -> the recepient is the 'device'.

1bRequest06The Request is `GET_DESCRIPTOR'
2wValueL00
3wValueH01The descriptor type is device(the values are defined in the USB spec)
4wIndexL00(The index is not relevant in this setup packet since there is only one device descriptor)
5wIndexH00
6wLengthL12Length of the data to be retrieved: 18(12h) bytes. (This is the length of the `device descriptor').
7wLengthH00

In response, the device sends the `Device Descriptor' data. For example, this is a `Device Descriptor' of `Cypress EZ-USB Integrated Circuit':

Byte No.012345678910
Content12010001ffffff40470580

Byte No.11121314151617
Content00010000000001

As defined in the USB specification, byte 0 indicates the length of the descriptor, bytes 2..3 contains the USB specification release number, byte 7 is the maximum packet size for endpoint 00, bytes 8..9 are the Vendor ID, bytes 10..11 are the Product ID, etc.

10.4  Performing Control Transfers with WinDriver

WinDriver allows you to easily send and receive control transfers on Pipe00, while using DriverWizard to test your device and within WinDriver API.

10.4.1  Control Transfers with DriverWizard

  1. Choose Pipe00 and click on `Read/Write to pipe'

    images/usb_control_transfer_1.png

  2. Enter the required Setup Packet. For a `Write' transaction that includes a data stage, enter the data in the `Input Data' field. Click `Read from Pipe' or `Write to Pipe' according to the required transaction.

    images/usb_control_transfer_2.png

  3. The `Device Descriptor' data retrieved from the device can be seen in DriverWizard log screen: (See the following image)

    images/usb_control_transfer_3.png

10.4.2  Control Transfers with WinDriver API

To perform a read or write transaction on the control pipe, you can either use the API generated by DriverWizard for your hardware, or directly call WinDriver WD_UsbTransfer function within your application.

DriverWizard generates the functions below(the functions can be found in the MyDevice_lib.c source file). Fill the setup packet in the BYTE SetupPacket[8] array (an element in the WD_USB_TRANSFER structure) and call these functions to send setup packets on Pipe00 and to retrieve control and status data from the device:

DWORD MY_DEVICE_ReadPipe00(MY_DEVICE_HANDLE hMY_DEVICE, 
        PVOID pBuffer, DWORD dwSize, CHAR setupPacket[8])
{
    WD_USB_TRANSFER transfer;
    DWORD i;
    BZERO(transfer);
    transfer.dwPipe = 0x00;
    transfer.dwBytes = dwSize;
    transfer.fRead = TRUE;
    for (i=0; i<8; i++)
        transfer.SetupPacket[i] = setupPacket[i];
    transfer.pBuffer = pBuffer;
    transfer.hDevice = hMY_DEVICE->hDevice;
    WD_UsbTransfer(hMY_DEVICE->hWD, &transfer);
    if (transfer.fOK)
        return transfer.dwBytesTransfered;
    return 0xffffffff;
}

DWORD MY_DEVICE_WritePipe00(MY_DEVICE_HANDLE hMY_DEVICE, 
       PVOID pBuffer, DWORD dwSize, CHAR setupPacket[8])
{
    WD_USB_TRANSFER transfer;
    DWORD i;
    BZERO(transfer);
    transfer.dwPipe = 0x00;
    transfer.dwBytes = dwSize;
    for (i=0; i<8; i++)
        transfer.SetupPacket[i] = setupPacket[i];
    transfer.pBuffer = pBuffer;
    transfer.hDevice = hMY_DEVICE->hDevice;
    WD_UsbTransfer(hMY_DEVICE->hWD, &transfer);
    if (transfer.fOK)
        return transfer.dwBytesTransfered;
    return 0xffffffff;
}

For further information regarding WD_UsbTransfer, please refer to Chapter 7 that illustrates the function reference for WinDriver in the WinDriver Manual.

Chapter 11
Improving Performance

11.1  Improving performance of your device driver - Overview

Once your User Mode driver has been written and debugged, you might find that certain modules in your code do not operate fast enough (for example - an interrupt handler or accessing IO mapped regions). If this is the case, try to improve the performance by any one of the two ways suggested in this chapter.

  1. Improve the performance of your User Mode driver.
  2. Move the performance critical parts of your code into WinDriver's Kernel PlugIn.

Note that the Kernel PlugIn is not implemented under Windows CE and VxWorks since in these OSes there is no separation between kernel mode and user mode. As such, top performance can be achieved without using the Kernel PlugIn.

Use the checklist below to determine how the performance should be improved in your driver.

11.2  Performance improvement checklist

The following ``Checklist'' will help you determine how to improve the performance of your driver:

  1. Create your driver in the User Mode as explained in the previous Chapters of this manual.
  2. Compile and debug your driver in the User Mode.
  3. When working in the User Mode, performance may take a back-seat. Check if you have performance problems. If you do not have any performance problems, you have finished your driver development.

If you do have performance problems:

Identify which part of the code the performance problem is at. Classify and solve the problem according to the table that follows:

ProblemSolution
#1ISA Card - Accessing an IO mapped range on the card-Try to convert multiple calls to WD_Transfer() to one call to WD_MultiTransfer()

(See the `Improving performance - Accessing IO mapped regions' section later in this Chapter).

-If this does not solve the problem, handle the IO at Kernel Mode, by writing a Kernel PlugIn.

(See the Kernel PlugIn related chapters for details)

#2PCI Card - Accessing an IO mapped range on the cardFirst, try to change the card from IO mapped to memory mapped by changing bit 0 of the address space PCI configuration register to 0 and then try the solutions for problem #3. You will probably need to re-program the EPROM to initialize BAR0/1/2/3/4/5 registers with different values.

-If this is not possible, try the solutions suggested for problem #1.

-If this does not solve the problem, handle the IO at Kernel Mode, by writing a Kernel PlugIn.

(See the Kernel PlugIn related chapters for details)

ProblemSolution
#3Accessing a memory mapped range on the card-Try to access memory without using WD_Transfer() by using direct access to memory mapped regions

(See the `Improving Performance -using direct access to memory mapped regions' section later in this Chapter).

-If this does not solve the problem, then there is a hardware design problem. You will not be able to increase performance by using any software design method, or by writing a Kernel PlugIn, or even by writing a full kernel driver.

#4 Interrupt latency. (Missing interrupts, Receiving interrupts too late)You need to handle the interrupts at Kernel Mode, by writing a kernel PlugIn.

(See the Kernel PlugIn related chapters for details)

#5USB devices: Slow transfer rateTo increase the transfer rate try to increase the packet size by choosing a different device configuration.

If there is need to do many small transfers, the Kernel-Plugin can be used.

11.3  Improving the performance of your User Mode driver

As a general rule, transfers to memory mapped regions are faster than transfers to I/O mapped regions. The reason is that WinDriver enables the user to directly access the memory mapped regions, without calling the WD_Transfer() function.

11.3.1  Using direct access to memory mapped regions

After registering a memory mapped region, via WD_CardRegister(), two results are returned: dwTransAddr and dwUserDirectAddr.
dwTransAddr should be used as a base address when calling WD_Transfer() to read or write to the memory region. A more efficient way to perform memory transfers would be to use dwUserDirectAddr directly as a pointer, and access with it the memory mapped range. This method enables you to read/write data to your memory-mapped region without any function calls overhead (i.e. Zero performance degradation).

11.3.2  Accessing IO mapped regions

The only way to transfer data on IO mapped regions is by calling WD_Transfer() function. If a large buffer needs to be transferred, the String (Block) Transfer commands can be used. For example: RP_SBYTE - ReadPort String Byte command will transfer a buffer of Bytes to the IO port. In such a case the function calling overhead is negligible compared to the block transfer time.

In a case where many short transfers are called, the function calling overhead may increase to an extent of overall performance degradation. This may happen if you need to call WD_Transfer() more than 20,000 calls per second.

An example for such a case could be: A block of 1MB of data needs to be transferred Word by Word, where in each word that is transferred, first the LOW byte is transferred to IO port 0x300, then the HIGH byte is transferred to IO port 0x301.

Normally this would mean calling WD_Transfer() 1 million times - Byte 0 to port 0x300, Byte 1 to port 0x301, Byte 2 to port 0x300 Byte 3 to port 0x301 etc (WP_BYTE - Write Port Byte).

A quick way to save 50% of the function call overhead would be to call WD_Transfer() with a WP_SBYTE (Write Port String Byte), with two bytes at a time. First call would transfer Byte0 and Byte1 to ports 0x300 and 0x301,

Second call would transfer Byte2 and Byte3 to ports 0x300 and 0x301 etc. This way, WD_Transfer() will only be called 500,000 times to transfer the block.

The third method would be by preparing an array of 1000 WD_TRANSFER commands. Each command in the array will have a WP_SBYTE command that transfers two bytes at a time. Then you call WD_MultiTransfer() with a pointer to the array of WD_TRANSFER commands. In one call to WD_MultiTransfer() - 2000 bytes of data will be transferred. To transfer the 1MB of data you will need only 500 calls to WD_Transfer(). This is 0.5% of the original calls to WD_Transfer(). The trade off in this case is the memory that is used to set-up the 1000 WD_TRANSFER commands.

Chapter 12
Kernel PlugIn

This chapter provides you with a brief description of the ``Kernel PlugIn'' feature of WinDriver.

12.1  Background

There are some distinct advantages with creating your driver in User Mode.

However, this imposes a fair amount of function call overhead from the Kernel to the User mode and then performance may not be acceptable. In such cases, the Kernel PlugIn feature allows critical section of the driver code to be moved to the kernel while keeping most of the code intact. Using WinDriver's Kernel PlugIn feature, your driver will operate without any degradation in performance.

The advantages of writing a Kernel PlugIn driver over a Kernel Mode driver are:

  1. All the driver code is written and debugged in User Mode.
  2. The code segments that are moved to the Kernel Mode remain essentially the same and therefore no Kernel debugging is needed.
  3. The parts of the code that will run in the kernel through the Kernel PlugIn are platform independent and therefore will run on all platforms supported by WinDriver. A standard kernel mode driver will run only on the platform it was written for.

Using WinDriver's Kernel PlugIn feature, your driver will operate without any degradation.

12.2  Do I need to write a Kernel PlugIn?

Not every performance problem requires you to write a Kernel PlugIn. Some performance problems can be solved in the User Mode driver, by better utilization of the features that WinDriver provides.

Chapter 11 guides you in solving your performance issues. This chapter contains a table to help you determine solutions for your driver's performance problems. In some cases a quick User Mode solution is provided, and in other cases, a Kernel Mode PlugIn must be written.

12.3  What kind of Performance can I expect

Since you can write your own interrupt handler in the kernel with the WinDriver Kernel PlugIn, you can expect to handle about 100,000 interrupts per sec without missing any one of them.

12.4  Overview of the development process

Using the WinDriver Kernel PlugIn, the developer first develops and debugs the driver in the User Mode with the standard WinDriver tools. After identifying the performance critical parts of the code (such as the interrupt handler, access to I/O mapped memory ranges, or slow data transfer rate through the USB pipes, etc.), the developer can `drop' these parts of code into WinDriver's Kernel PlugIn ( which runs in the Kernel Mode) thereby eliminating calling overhead. This unique feature allows the developer to start with quick and easy development in the User Mode, and progress to performance oriented code only where needed. This unique architecture saves time, and allows absolutely zero performance degradation.

Chapter 13
WinDriver Kernel PlugIn Architecture

This chapter explains the architectural details of the ``Kernel PlugIn'' feature of WinDriver.

13.1  Introduction

A driver written in the User Mode uses WinDriver's functions(WD_xxx functions) for device access. If a certain function in the User Mode needs to achieve kernel performance (the interrupt handler for example), that function is moved to the WinDriver KernelPlugIn. The code will still work ``as is'' since WinDriver exposes its WD_xxx interface to the Kernel PlugIn as well.

images/KPArch.png

Figure 13.1: KernelPlugIn Architecture

13.2  WinDriver Kernel and Kernel Plugin Interaction

There are two types of interaction between the WinDriver Kernel and the WinDriver Kernel PlugIn. They are:

  1. Interrupt handling: When WinDriver receives an interrupt, it will activate the interrupt handler in the User Mode by default. However, if the interrupt was set to be handled by the WinDriver Kernel PlugIn, then once WinDriver receives the interrupt, it is processed by the interrupt function in the Kernel. This is the same code that you wrote and debugged in the User Mode interrupt handler before.
  2. Message passing: To execute functions in the Kernel Mode (such as I/O processing functions), the user mode driver simply passes a ``message'' to the WinDriver Kernel PlugIn. This message is mapped to a specific function, which is then executed in the kernel. This function contains the same code as it did when it was written and debugged in the User Mode.

13.3  Kernel Plugin Components

At the end of your Kernel PlugIn development cycle, you have the following elements to your driver:

  1. Your User Mode driver - written with the WD_xxx functions.
  2. The WinDriver Kernel - Windrvr.sys or Windrvr.vxd and wdpnp.sys for USB drivers.
  3. Your Kernel PlugIn - <Your Driver Name>.sys or <Your Driver Name>.vxd - this is the driver that contains the functionality that you have chosen to bring down to the Kernel level.

13.4  Kernel PlugIn Event Sequence

The following is a typical event sequence that covers all the functions that you can implement in your Kernel PlugIn(KP):

Event / CallbackRemarks
Event: Windows loads your Kernel PlugIn driverAt boot time, or by dynamic loading, or as instructed by the registry.
KP Call-back: Your KP_Init() Kernel PlugIn function is calledKP_Init() informs WinDriver of the name of your KP_Open() routine. WinDriver will call this routine when the applicaton wishes to open your driver (when it calls WD_KernelPlugInOpen())
Event: Your app(User Mode driver) calls WD_KernelPlugInOpen()
KP Call-back: Your KP_Open() routine is calledIn your KP_Open() function, you inform WinDriver of the names of all the call-back functions that you have implemented in your KP driver, and initiate the KP driver if needed.

Event / CallbackRemarks
Event: Your app calls WD_KernelPlugInCall()Your app calls WD_KernelPlugInCall() to run code in the Kernel Mode(in the KP driver). The app passes a message to the KP driver. The KP driver will select the function to execute according to the message sent.
KP Call-back: Your KP_Call() routine is called.Executes code according to the message passed to it from the User Mode.
Event: Your hardware creates an interrupt
KP Call-back: Your KP_IntAtIrql() routine is called. (If the KP interrupts are enabled KP_IntAtIrql() runs at a high priority, and therefore should do only the basic interrupt handling(such as lowering the HW interrupt signal). If more interrupt processing is needed, it is deferred to the KP_IntAtDpc() function. If your KP_IntAtIrql() function returns a value greater than 0, your KP_IntAtDpc() function is called.

Event / CallbackRemarks
Event: KP_IntAtIrql() function returns a value greater than 0Needs interrupt code to be processed as a deferred procedure call in the kernel
KP_Call-back: KP_IntAtDpc() is calledProcesses the rest of the interrupt code, but at a lower priority than KP_IntAtIrql
Event: KP_IntAtDpc() returns a value greater than 0Needs interrupt code to be processed in the User Mode as well
KP_Call-back: WD_Intwait() returns.Execution resumes at your User Mode interrupt handler.

Chapter 14
Kernel PlugIn - How it works

This chapter takes you through the development cycle of a Kernel PlugIn. It assumes that you have already written and debugged your entire driver code in the User Mode, and have encountered a performance problem.

14.1  Minimal Requirements for creating a Kernel PlugIn

NOTE: Windows NT/2000 require SYS files for Kernel PlugIn. Windows 95/98/ME need VXD files. SYS files cannot be used on Windows 98/ME as Kernel PlugIn in Version 5.0 and below.

NT DDK can be downloaded (Free) at http://www.microsoft.com/hwdev/ddk/ddk40.htm.

14.2  Directory structure and sample for the WinDriver Kernel PlugIn

\windriver\kerplug\lib - includes the files needed to link your Kernel PlugIn
\windriver\kerplug\kptest - contains a sample minimal Kernel PlugIn driver.
The sample implements a passing data function to / from the kernel driver and a kernel mode interrupt handler.

The data exchange function gets the version of the WinDriver kernel module and passes it to the user level. This sample can be a base to implement I/O calls with the Kernel PlugIn.

The interrupt handler implements an interrupt counter. The interrupt handler counts five interrupts and notifies the user mode only on one out of every five incoming interrupts.(more details can be found in windriver\kerplug\kptest\files.txt).

KPTest_com.h contains common definitions such as messages, between the Kernel PlugIn and the User-mode.

\windriver\kerplug\kptest\usermode - User-mode component of the driver
\windriver\kerplug\kptest\kermode - Kernel PlugIn driver

14.3  Kernel PlugIn implementation

14.3.1  Before you begin

The following functions are call-back functions which you will implement in your Kernel PlugIn driver, and which will be called when their `calling' event occurs. For example, KP_Init() is the call-back function which is called when the driver is loaded. Any code that you want to execute upon loading should be in this function.

In KP_Init(), the name of your driver is given. From then on, all of the call-backs which you implement in the kernel will contain your driver's name. For example, if your driver's name is MyDriver, then your `Open' call-back will be called MyDriver_Open(). It is the convention of this reference guide to mark these functions as KP_ functions - i.e. the `Open' function will be written here as KP_Open(), where the KP replaces your driver's name.

14.3.2  Write your KP_INIT() function

In your kernel driver you should implement the following function:

BOOL __cdecl KP_Init(KP_INIT *kpInit);

where KP_INIT is the following structure:

typedef struct { 
    DWORD dwVerWD;           // Version of library WD_KP.LIB 
    CHAR cDriverName[9];     //  driver name, up to 8 chars. 
    KP_FUNC_OPEN funcOpen;   // The KP_Open function 
} KP_INIT; 

This function is called once, when the driver is loaded. The kpInit structure should be filled out with the KP_Open function and the name of your Kernel PlugIn. (see example in KPTest.c). Note that the name that you choose for your KP driver (by setting it in the kpInit structure), should be the same name as the driver you are creating.

For example, if you are creating a driver called ABC.VXD or ABC.SYS, then you should pass the name ABC in the kpInit structure.

From the KPTest Sample:

BOOL __cdecl KP_Init(KP_INIT *kpInit) 
{ 
    // check if the version of WD_KP.LIB is the same version 
    // as WINDRVR.H and WD_KP.H 

    if (kpInit->dwVerWD!=WD_VER) 
    {  
       // you need to re-compile your kernel plugin with
       // the compatible version of WD_KP.LIB, WINDRVR.H 
       // and WD_KP.H! 
       return FALSE; 
    } 

    kpInit->funcOpen = KPTest_Open; 
    strcpy (kpInit->cDriverName, "KPTest"); 
    return TRUE; 
} 

14.3.3  Write your KP_OPEN() function

In your Kernel PlugIn file, implement the KP_Open() function, where KP is the name of your KP driver (copied to kpInit->cDriverName in the KP_Init() function.

BOOL __cdecl KP_Open(KP_OPEN_CALL *kpOpenCall, HANDLE hWD, PVOID pOpenData, PVOID *ppDrvContext);

This call-back is called when the User Mode application calls the WD_KernelPlugInOpen() function.

In the KP_Open() function, define the call-backs that you wish to implement in the Kernel PlugIn.

Following is a list of the call-backs which can be implemented:

Call-back NameFunctionality
KP_Close()[16.2.3]Called when the User Mode application calls the WD_KernelPlugInClose()[16.1.2] Function
KP_Call()[16.2.4]Called when the User Mode application calls the WD_KernelPlugInCall()[16.1.3] function. This function is a message handler for your utility functions.
KP_IntEnable()[16.2.5] Called when the User Mode application calls the WD_IntEnable()[16.1.4] function. This function should contain any initialization needed for your Kernel PlugIn interrupt handling
KP_IntDisable()[16.2.6]Called when the User Mode application calls the WD_IntDisable()[7.18] function. This function should free any memory which was allocated in the KP_IntEnable()[16.2.5] callback.
KP_IntAtIrql()[16.2.7] Called when WinDriver receives an interrupt. This is the function that will handle your interrupt in the Kernel Mode.
KP_IntAtDpc()[16.2.8] Called if the KP_IntAtIrql()[16.2.7] callback has requested deferred handling of the interrupt (by returning with a value of TRUE).

These handlers will later be called when the user-mode program opens a KP driver (WD_KernelPlugInOpen(), WD_Kernel PlugInClose()), sends a message
(WD_KernelPlugInCall()), or installs an interrupt where hKernelPlugIn passed to
WD_IntEnable() is of a Kernel PlugIn driver opened with WD_KernelPlugInOpen().

From the KPTest Sample:

BOOL __cdecl KPTest_Open(KP_OPEN_CALL *kpOpenCall, 
              PVOID pOpenData, PVOID *ppDrvContext) 
{ 
    kpOpenCall->funcClose = KPTest_Close; 
    kpOpenCall->funcCall = KPTest_Call; 
    kpOpenCall->funcIntEnable = KPTest_IntEnable; 
    kpOpenCall->funcIntDisable = KPTest_IntDisable; 
    kpOpenCall->funcIntAtIrql = KPTest_IntAtIrql; 
    kpOpenCall->funcIntAtDpc = KPTest_IntAtDpc;
    *ppDrvContext = NULL; // you can allocate memory here 
    return TRUE; 
} 

14.3.4  Write the remaining PlugIn call-backs

Add your specific code inside the call backs routines.

14.4  KPTest - A sample Kernel PlugIn Driver

The KPTest directory (\WinDriver\kerplug\KPTest) contains a sample minimal Kernel PlugIn driver which you can compile and execute. Use this sample as your skeletal Kernel PlugIn driver.

This sample builds KPTest.VXD and KPTest.SYS, and KPTest.EXE. The sample demonstrates communication between your application (KPTest.EXE) and your Kernel PlugIn (KPTest.VXD or KPTest.SYS).

The KPTest sample in this directory, is a Kernel PlugIn which implements a ``Get Version'' function, to demonstrate passing data (messages) to/from the Kernel PlugIn. It also implements an interrupt handler in the kernel. This Kernel PlugIn is called by the User Mode driver called KPTest.EXE.

NOTE: To check that you are ready to build a Kernel PlugIn driver, it is recommended to build and run this project first, before continuing to write your own Kernel PlugIn.

14.5  Handling Interrupts in the Kernel PlugIn

Interrupts will be handled by the Kernel PlugIn, if a Kernel PlugIn handle was passed to WD_IntEnable() by the User Mode application when it enabled the interrupt. When WinDriver receives a hardware interrupt, it calls the KP_IntAtIrql() (if Kernel PlugIn interrupts are enabled). In the KPTest sample, the interrupt handler running in the Kernel PlugIn counts 5 interrupts, and notifies the user-mode only of one out of each 5 incoming interrupts. This means that WD_IntWait() (in the user-mode) will return only on one out of 5 incoming interrupts.

14.5.1  Interrupt handling in user mode (without Kernel PlugIn)

If the Kernel PlugIn interrupt handle is NOT enabled, then each incoming interrupt will cause WD_IntWait() to return. See drawing below:

images/img13wd.png

Figure 14.1: Interrupt Handling without Kernel PlugIn

14.5.2  Interrupt handling in the Kernel (with the Kernel PlugIn)

To instruct the interrupts to be handled by the Kernel PlugIn, the Kernel PlugIn handle must be given as a parameter to the WD_IntEnable() function. This enables the Kernel PlugIn interrupt handler.

If the Kernel PlugIn interrupt handler is enabled, then KP_IntAtIrql() will be called on each incoming interrupt. The code in the KP_IntAtIrql() function is executed at IRQL. While this code is running, the system is halted (i.e. there will be no context switch and no lower priority interrupts will be handled). The code in the KP_IntAtIrql() function is limited to the following restrictions:

images/img14wd.png

Figure 14.2: Interrupt Handling with the Kernel PlugIn

Therefore, the code in KP_IntAtIrql() should be kept to a minimum, while the rest of the code that you want to run in the interrupt handler should be written in the KP_IntAtDpc(), which is called after IRQL finishes. The code in KP_IntAtDpc() is not limited by the above restrictions.

14.6  Message passing

The WinDriver architecture enables calling a Kernel Mode function from the User Mode by passing a message through the WD_KernelPlugInCall() function. The messages are defined by the developer in a header file that is common to both the User Mode and Kernel Mode Plugin parts of the driver. This header file is called KPxxx_COM.H by convention - the corresponding header header file in the KPTest sample is called KPTest_COM.H. Upon receiving the message, WinDriver Kernel PlugIn executes the KP_Call function which maps a function to this message.

In the KPTest sample, the GetVersion function is a simple function which returns an arbitrary integer and string (which simulates your KPTest's version). This function will be called by the Kernel PlugIn, whenever the Kernel PlugIn receives a `GetVersion' message from the KPTest.EXE. You can see the definition of the message KPTEST_MSG_VERSION in the header file KPTEST_COM.H

The KPTest.EXE sends the message using the WD_Kernel PlugInCall() function.

Chapter 15
Writing a Kernel PlugIn

The Kernel PlugIn directory (\windriver\kerplug) contains a sample Kernel PlugIn driver called KP_Test. The sample demonstrates communication between your application (KPTest.EXE) and your Kernel PlugIn (KPTest.VXD or KPTest.SYS).

The easiest way to write a Kernel PlugIn driver is to use this example as the skeletal code for your driver.

The following is a step by step guide to creating your kernel driver. The KPTest sample code will be used to demonstrate the different stages:

15.1  Determining whether a Kernel PlugIn is needed

  1. The Kernel PlugIn should be used only after your driver code has been written and debugged in the User Mode. This way, all of the logical problems of creating a device driver are solved in the User Mode, where development and debugging are much easier.
  2. Determine whether a Kernel PlugIn should be written by consulting chapter 11 that explains how to improve the performance of your driver.

15.2  Preparing the User Mode source code

  1. Isolate the function or functions that you need to move into the Kernel PlugIn.
  2. Remove any platform specific code from the function. Use only the WinDriver functions which may be used from the kernel as well (See details later on in this chapter).
  3. Compile and debug your driver in the User Mode again, to see that your code still works after these changes are made.

15.3  Creating a new Kernel PlugIn Project

  1. Make a copy of the KPTest directory. For example, to create a new project called MyDrv, copy \WinDriver\kerplug\KPTest to \WinDriver\kerplug\MyDrv.
  2. Change all instances of KPTest in all the files in your new directory to MyDrv. (You may use the `find in files' option in MSDEV for this).
  3. Change all occurunces of KPTest in file names to MyDrv.

15.4  Creating a handle to the WinDriver Kernel PlugIn

In your original User Mode source code, call WD_Kernel PlugInOpen() at the beginning of your code, and WD_KernelPlug InClose() before terminating.

15.5  Interrupt Handling in the Kernel PlugIn

  1. When calling WD_IntEnable(), give the handle to the Kernel PlugIn that you received from opening the Kernel PlugIn
  2. Move the source code in the User Mode interrupt handler to the Kernel PlugIn, by moving some of it to KP_IntAtIrql() and some of it to KP_IntAtDpc() (See Section 14.5 for an explanation on handling interrupts in the kernel).

15.6  I/O handling in the Kernel PlugIn

  1. Move your I/O handling code from the User Mode to KP_Call().
  2. To call this code in the kernel from the User Mode, use WD_KernelPlugInCall(), with the Kernel PlugIn handle, and a message for each of the different functionalities you need. For each functionality, create a different message.
  3. Define these messages in the file KPTest_Com.H, which is a common header file, between the kernel-mode and the user-mode. This file should have the message definitions (IDs) and data structures used to communicate between the kernel-mode and user-mode.

15.7  Compiling your Kernel PlugIn Driver

Run compile.bat to compile and link your KP driver.

15.8  Installing your Kernel PlugIn Driver

Copy the relevant file to your drivers directory.

On Win32 Platforms:

Register / Load your driver: The Kernel PlugIn driver is dynamically loadable, and therefore you need not re-boot in order to run your driver.

On all Win32 platforms, you need to run WDREG.EXE to install the driver:

(In the following instructions, NAME stands for your Kernel PlugIn driver name).

On Win95 and WinNT:

Run c:\WinDriver\util\WDREG -name NAME install (in this example run WDREG -name mydrv install) to register and load your driver. For further instructions see chapter 20 that explains how to dynamically load your driver .

On Win98

To install the SYS file, run c:\WinDriver\util\
WDREG -name NAME install(in this example run WDREG -name mydrv install).

To install the VXD file (note the -vxd flag), run c:\WinDriver\util\WDREG -vxd -name NAME install

On Linux

  1. Copy the driver created to the modules directory: For example: kptest/LINUX# cp kptest_module.o/lib/modules/misc/
  2. Insert the module into the kernel: For example: kptest/LINUX#/sbin/insmod kptest_module.o

On Solaris

  1. Copy the driver created to the drivers directory: For example: kptest/SOLARIS# cp kptest/kernel/drv
  2. Install the driver: For example: kptest/SOLARIS# add_drv kptest

Chapter 16
Kernel PlugIn Function reference

16.1  User Mode functions

The following functions are the User Mode functions which initiate the Kernel PlugIn's operation, and activate its call-backs.

16.1.1  WD_KernelPlugInOpen()

This function is used to obtain a valid handle for the Kernel PlugIn.

PROTOTYPE

void WD_KernelPlugInOpen(HANDLE hWD, WD_KERNEL_PLUGIN *pKernelPlugIn);

PARAMETERS

(WD_KERNEL_PLUGIN elements)

hKernelPlugIn - returns the handle of the Kernel PlugIn.
pcDriverName - name of Kernel PlugIn to load, up to 8 chars
pcDriverPath - file name of Kernel PlugIn to load. If NULL the driver will be searched in the windows system directory with the name in pcDriverName.
pOpenData - pointer to data that will be passed to KP_Open() callback in the Kernel PlugIn.

RETURN VALUE

None

EXAMPLE

// Handle to the KernelPlugIn 

WD_KERNEL_PLUGIN kernelPlugIn; 

BZERO (kernelPlugIn); // Tells WinDriver which driver to open 

kernelPlugIn.pcDriverName = "KPTEST"; // Opens KPTEST.SYS/VXD 

WD_KernelPlugInOpen(hWD, &kernelPlugIn); 

if (!kernelPlugIn.hKernelPlugIn) 
{ 
 printf ("There was an error loading driver: %s\n", 
           kernelPlugIn.pcDriverName); 
 return ; 
} 
printf("Kernel PlugIn opened\n"); 

16.1.2  WD_KernelPlugInClose()

Closes the WinDriver Kernel PlugIn handle obtained from WD_KernelPlugInOpen().

PROTOTYPE

void WD_KernelPlugInClose(HANDLE hWD,WD_KERNEL_PLUGIN *pKernelPlugIn);

PARAMETERS

(WD_KERNEL_PLUGIN elements)

hKernelPlugIn - handle of the Kernel PlugIn to close.

RETURN VALUE

None

EXAMPLE

WD_KernelPlugInClose(hWD, &kernelPlugIn); 

16.1.3  WD_KernelPlugInCall()

Calls a routine in the Kernel PlugIn to be executed.

Calling the WD_KernelPlugInCall() function in the User Mode, calls your KP_Call() callback function in the Kernel Mode. Your KP_Call() function in the Kernel PlugIn will decide what routine to execute according to the message passed to it in the WD_KERNEL_PLUGIN_CALL structure.

PROTOTYPE

void WD_KernelPlugInCall( HANDLE hWD, WD_KERNEL_PLUGIN_CALL *pKernelPlugInCall);

PARAMETERS

(WD_KERNEL_PLUGIN_CALL elements)

hKernelPlugIn - handle of the Kernel PlugIn.
dwMessage - message ID to pass to KP_Call() callback.
pData - pointer to data to pass to KP_Call() callback.
dwResult - value set by KP_Call() callback.

RETURN VALUE

None

EXAMPLE

WD_KERNEL_PLUGIN_CALL kpCall; 

BZERO (kpCall);    // Prepare the kpCall structure 
//from WD_KernelPlugInOpen() 
kpCall.hKernelPlugIn = hKernelPlugIn; 
// The message to pass to KP_Call(). This will determine 
// the action performed in the kernel. 
kpCall.dwMessage = MY_DRV_MSG_VERSION; 

kpCall.pData = &mydrvVer;   // The data to pass to the call. 
WD_KernelPlugInCall(hWD, &kpCall); 

16.1.4  WD_IntEnable()

If the handle passed to this function is of a Kernel PlugIn, then that Kernel PlugIn will handle all the interrupts.

In this case, upon receiving the interrupt, your Kernel Mode KP_IntAtIrql() function will execute. If this function returns a value greater than 0, then your deferred procedure call, KP_IntAtDpc() , will be called.

PROTOTYPE

void WD_IntEnable(HANDLE hWD,WD_INTERRUPT *pInterrupt);

PARAMETERS

(WD_INTERRUPT elements)

textbfkpCall- information on Kernel PlugIn to install as interrupt handler.

textbfkpCall.hKernelPlugIn- handle of Kernel PlugIn. if zero, then no Kernel PlugIn interrupt handler is installed.

textbfkpCall.dwMessage- message ID to pass to KP_IntEnable() callback.

textbfkpCall.pData- pointer to data to pass to KP_IntEnable() callback.

textbfkpCall.dwResult- value set by KP_IntEnable() callback.

For information about all other parameters of WD_IntEnable(), see the documentation of WD_IntEnable() in Chapter 7 explaining the WinDriver functions.

RETURN VALUE

None

EXAMPLE

WD_INTERRUPT Intrp; 

BZERO(Intrp); // from WD_CardRegister() 

Intrp.hInterrupt = hInterrupt; 

Intrp.Cmd = NULL; 

Intrp.dwCmds = 0; 

Intrp.dwOptions = 0; // from WD_KernelPlugInOpen() 

Intrp.kpCall.hKernelPlugIn = hKernelPlugIn; 

WD_IntEnable(hWD, &Intrp); 

if (!Intrp.fEnableOk) 
 printf ("failed enabling interrupt\n"); 

16.2  Kernel functions

The following functions are call-back functions which you will implement in your Kernel PlugIn driver, and which will be called when their `calling' event occurs. For example, KP_Init() is the call-back function which is called when the driver is loaded. Any code that you want to execute upon loading should be in this function.

In KP_Init(), the name of your driver is given, and its call-backs. From then on, all of the call-backs which you implement in the kernel will contain your driver's name. For example, if your driver's name is MyDriver, then your KP_Open call-back may be called MyDriver_Open(). It is the convention of this reference guide to mark these functions as KP_ functions - i.e. the `Open' function will be written here as KP_Open(), where the KP replaces your driver's name.

16.2.1  KP_Init()

You must define the KP_Init() function in your code in order to link the Kernel PlugIn driver to the WinDriver.

KP_Init() is called when the driver is loaded. Any code that you want to execute upon loading should be in this function.

PROTOTYPE

BOOL __cdecl KP_Init(KP_INIT *kpInit);

PARAMETERS

kpInit - structure to fill in the address of the KP_Open() callback function.

RETURN VALUE

TRUE if successful. If FALSE, then the Kernel PlugIn driver will be unloaded.

EXAMPLE

BOOL _cdecl KP_Init(KP_INIT *kpInit) 

{ 
  // check if the version of WD_KP.LIB is the same version as 
  //WINDRVR.H and WD_KP.H 
  if (kpInit->dwVerWD!=WD_VER) 
  { 
    
      // you need to re-compile your kernel plugin 
      //with the compatible version 
      // of WD_KP.LIB, 
      // WINDRVR.H and WD_KP.H! 
     return FALSE; 
  } 

  kpInit->funcOpen = KP_Open; 
  strcpy (kpInit->cDriverName, "KPTEST");  // until 8 chars 
  return TRUE; 
} 

16.2.2  KP_Open()

Called when WD_KernelPlugInOpen() is called from the User Mode. The pDrvContext returned will be passed to rest of the functions.

PROTOTYPE

BOOL __cdecl KP_Open(KP_OPEN_CALL *kpOpenCall, HANDLE hWD, PVOID pOpenData, PVOID *ppDrvContext);

PARAMETERS

kpOpenCall- structure to fill in the addresses of the KP_xxxx() callback functions

hWD- handle of WinDriver that WD_KernelPlugInOpen() was called with.

pOpenData- pointer to data, passed from user-mode.

ppDrvContext- pointer to driver context data with which KP_Close(), KP_Call() and KP_IntEnable() functions will be called. Use this to keep driver specific information.

RETURN VALUE

TRUE if successful. If FALSE, then the call to WD_KernelPlugInOpen() from user-mode will fail.

EXAMPLE

BOOL _cdecl KP_Open(KP_OPEN_CALL *kpOpenCall, HANDLE hWD,
            PVOID pOpenData, PVOID *ppDrvContext) 

{ 
    kpOpenCall->funcClose = KP_Close; 
    kpOpenCall->funcCall = KP_Call; 
    kpOpenCall->funcIntEnable = KP_IntEnable; 
    kpOpenCall->funcIntDisable = KP_IntDisable; 
    kpOpenCall->funcIntAtIrql = KP_IntAtIrql; 
    kpOpenCall->funcIntAtDpc = KP_IntAtDpc; 
    *ppDrvContext = NULL; // you can allocate memory here 
    return TRUE; 
} 

16.2.3  KP_Close()

Called when WD_KernelPlugInClose() is called from the User Mode.

PROTOTYPE

void __cdecl KP_Close(PVOID pDrvContext);

PARAMETERS

pDrvContext - driver context data that was set by KP_Open().

RETURN VALUE

None

EXAMPLE

void _cdecl KP_Close(PVOID pDrvContext) 
{ 
 // you can free the memory allocated for pDrvContext here
} 

16.2.4  KP_Call()

Called when the User Mode application calls the WD_Kernel PlugInCall() function. This function is a message handler for your utility functions.

PROTOTYPE

void __cdecl KP_Call(PVOID pDrvContext, WD_KERNEL_PLUGIN_CALL *kpCall, BOOL fIsKernelMode);

PARAMETERS

pDrvContext - driver context data that was set by KP_Open().
kpCall - structure with information from WD_Kernel PlugInCall().
kpCall.dwMessage - message ID passed from WD_Kernel PlugInCall().
kpCall.pData - pointer to data passed from WD_Kernel PlugInCall().
kpCall.dwResult - value to return to WD_Kernel PlugInCall().
fIsKernelMode - this parameter is passed by the Windriver kernel.

RETURN VALUE

None

EXAMPLE

void _cdecl KP_Call(PVOID pDrvContext, 
   WD_KERNEL_PLUGIN_CALL *kpCall, BOOL fIsKernelMode) 
{ 
    kpCall->dwResult = MY_DRV_OK; 
    switch ( kpCall->dwMessage ) 
    { 
      // in this sample we implement a GetVersion message 
    case MY_DRV_MSG_VERSION: 
    { 
      MY_DRV_VERSION *ver = (MY_DRV_VERSION *) 
         kpCall->pData; 
      COPY_TO_USER_OR_KERNEL(&ver->dwVer, &dwVer, 
          sizeof(DWORD), fIsKernelMode);
      COPY_TO_USER_OR_KERNEL(ver->cVer, "My Driver V1.00", 
          sizeof("My Driver V1.00")+1, fIsKernelMode);
      kpCall->dwResult = MY_DRV_OK; 
    } 
    break; 
    // you can implement other messages here 
    default: 
      kpCall->dwResult = MY_DRV_NO_IMPL_MESSAGE; 
    } 
} 

NOTESThe fIsKernelMode parameter is passed by the Windriver kernel to the KP_Call routine. The user need not do anything about this parameter. But notice how this parameter is passed to the macro COPY_TO_USER_OR_KERNEL - this is required for the macro to function correctly. You may see the implementation of the macro COPY_TO_USER_OR_KERNEL in the header file kpstdlib.h, found under the directory WinDriver\Include of your WinDriver installation.

16.2.5  KP_IntEnable()

Called when WD_IntEnable() is called from the User Mode, with a Kernel PlugIn handler specified. The pIntContext will be passed to the rest of the functions that handle interrupts.

This function should contain any initialization needed for your Kernel PlugIn interrupt handling.

PROTOTYPE

BOOL __cdecl KP_IntEnable (PVOID pDrvContext, WD_KERNEL_PLUGIN_CALL *kpCall, PVOID *ppIntContext);

PARAMETERS

pDrvContext- driver context data that was set by KP_Open().

kpCall- structure with information from WD_IntEnable().

kpCall.dwMessage- message ID passed from WD_IntEnable().

kpCall.pData- pointer to data passed from WD_IntEnable().

kpCall.dwResult- value to return to WD_IntEnable().

ppIntContext- pointer to interrupt context data that KP_Int Disable(), KP_IntAtIrql() and KP_IntAtDpc() functions will be called with. Use this to keep interrupt specific information.

RETURN VALUE

Returns TRUE if enable is successful.

EXAMPLE

BOOL _cdecl KP_IntEnable(PVOID pDrvContext, 
     WD_KERNEL_PLUGIN_CALL *kpCall, PVOID *ppIntContext) 
{ 

  // you can allocate memory specific for each interrupt 
  //in ppIntContext 

  *ppIntContext = NULL; 

  return TRUE; 

} 

16.2.6  KP_IntDisable()

Called when the User Mode application calls the WD_IntDisable() function. This function should free any memory which was allocated in the KP_IntEnable().

PROTOTYPE

void __cdecl KP_IntDisable(PVOID pIntContext);

PARAMETERS

pIntContext - interrupt context data that was set by KP_Enable().

RETURN VALUE None

EXAMPLE

void _cdecl KP_IntDisable(PVOID pIntContext) 
{ 

  // you can free the interrupt specific 
  //memory in pIntContext here 

} 

16.2.7  KP_IntAtIrql()

This is the function which will run at IRQL if the Kernel PlugIn handle is passed when enabling interrupts.

Code running at IRQL will only be interrupted by higher priority interrupts.

Code running at IRQL is limited by the following restrictions:

  1. You may only access non-pageable memory.
  2. You may only call the following functions: WD_Transfer(), specific DDK functions which are allowed to be called from an IRQL.
  3. You may not call malloc(), free(), or any WD_xxx command (other than WD_Transfer()).

The code performed at IRQL should be minimal (e.g. only the code which acknowledges the interrupt), since it is operating at a high priority. The rest of your code should be written at KP_AtDpc(), in which the above restrictions do not apply.

PROTOTYPE

BOOL __cdecl KP_IntAtIrql(PVOID pIntContext, BOOL *pfIsMyInterrupt);

PARAMETERS

pIntContext- interrupt context data that was set by KP_Int Enable().

pfIsMyInterrupt- set this to TRUE, if the interrupt belongs to this driver, or FALSE if not. If you are not sure, it is safest to return FALSE.

RETURN VALUE

Returns TRUE if DPC function is needed for execution.

EXAMPLE

static DWORD G_dwInterruptCount = 0; 

BOOL _cdecl KP_IntAtIrql(PVOID pIntContext, BOOL *pfIsMyInterrupt) 
{ 
  // you should check your hardware here to see 
  //if the interrupt belongs to you. 
  // if in doubt, return FALSE (this is the safest) 
  *pfIsMyInterrupt = TRUE; 
  // in this example we will schedule a DPC 
  //once in every 5 interrupts 
  G_dwInterruptCount ++; 
  if ((G_dwInterruptCount % 5) == 0 ) 
    return TRUE; 
  return FALSE; 

} 

16.2.8  KP_IntAtDpc()

This is the Deferred Procedure Call which is executed only if the KP_IntAtIrql() function returned true.

Most of your interrupt handler should be written at DPC.

PROTOTYPE

DWORD __cdecl KP_IntAtDpc(PVOID pIntContext, DWORD dwCount);

PARAMETERS

pIntContext- interrupt context data that was set by KP_Enable().

dwCount- the number of times KP_IntAtIrql() returned TRUE. If dwCount is 1,
then only KP_IntAtIrql() only requested once a DPC. If the value is greater, then KP_IntAtIrql()
has already requested a DPC a few times, but the interval was too short, therefore KP_IntAtDpc() was not called for each one of them.

RETURN VALUE

Returns the number of times to notify user-mode (i.e. return from WD_IntWait()).

EXAMPLE

DWORD _cdecl KP_IntAtDpc(PVOID pIntContext, DWORD dwCount) 
{ 

  // return WD_IntWait as many times as KP_IntAtIrql
  // scheduled KP_IntAtDpc() 

  return dwCount; 

} 

Chapter 17
Kernel PlugIn structure reference

17.1  User Mode structures

17.1.1  WD_KERNEL_PLUGIN

Defines a Kernel PlugIn open command.

Used by WD_KernelPlugInOpen()[16.1.1] and WD_KernelPlugInClose()[16.1.2].

MEMBERS:

TYPE NAME DESCRIPTION
DWORDhKernelPlug InHandle to Kernel PlugIn

TYPE NAME DESCRIPTION
PCHARpcDriverNameName of Kernel PlugIn driver. Should be no longer than 8 letters. Should not include the VXD or SYS extension.
PCHAR pcDriverPath The directory and file name in which to look for the KP driver. If NULL, then the driver will be searched for in the default windows system directory, under the name supplied in pcDriverName, with VXD added for Windows-95, or SYS added for Windows-NT
PVOIDpOpenDataData to pass to KP_Open() callback in the Kernel PlugIn

17.1.2  WD_INTERRUPT

Used to describe an interrupt.

Used by the following functions: WD_IntEnable()[16.1.4], WD_Int Disable()[7.18], WD_IntWait()[7.19], WD_IntCount()[7.20].

MEMBERS:

TYPE NAMEDESCRIPTION
WD_KERNEL_ PLUGIN_CALL [17.1.3]kpCall The kpCall structure contains the handle to the Kernel PlugIn and other information which should be passed to the Kernel mode interrupt handler when installing it

If the handle is zero, then interrupt is installed without a Kernel PlugIn interrupt handler.

For information about all other members of WD_INTERRUPT, see the documentation of this structure in Chapter 8 that explains the WinDriver structures.

17.1.3  WD_KERNEL_PLUGIN_CALL

Contains information about the Kernel PlugIn, which will be used when calling a utility Kernel PlugIn function or when installing an interrupt.

Used by WD_KernelPlugInCall()[16.1.3] and WD_IntEnable()[16.1.4].

MEMBERS:

TYPE NAMEDESCRIPTION
DWORD hKernelPlugInhandle to Kernel PlugIn.
DWORDdwMessagemessage ID to pass to KernelPlugIn callback
PVOID pDatapointer to data to pass to Kernel PlugIn callback.
DWORD dwResultvalue set by Kernel PlugIn callback, to return back to User Mode.

17.2  Kernel Mode structures

17.2.1  KP_INIT

The KP_INIT structure is used by your KP_Init()[16.2.1] function in the Kernel PlugIn. Its primary use is for notifying WinDriver what the name of the driver will be, and which Kernel Mode function to call when the application calls WD_KernelPlugInOpen()[16.1.1].

MEMBERS:

TYPENAME DESCRIPTION
DWORD dwVerWDVersion of WinDriver library WD_KP.LIB.
CHARcDriver Name[9] The device driver name, upto 8 chars.
KP_FUNC_OPENfuncOpenThe KP_Open() Kernel Mode function which WinDriver should call when the application calls WD_KernelPlugInOpen()

17.2.2  KP_OPEN_CALL

This is the structure through which the Kernel PlugIn defines the names of the call-backs which it implements. It is used in the KP_Open() Kernel PlugIn function.

A kernel PlugIn may implement 6 different call-back functions:

funcClose - Called when application is done with this instance of the driver.
funcCall - Called when the application calls the WD_Kernel PlugInCall function. This function is the `general purpose' function. In it, implement any functions which should run in the Kernel Mode, (besides the Interrupt handler which is a special case). The funcCall will determine which function to execute according to the message passed to it.
funcIntEnable - Called when application calls the WD_Kernel PlugInIntEnable(). This call-back function should initiate any activity which needs to be done when enabling an interrupt.
funcIntDisable - The cleanup function which is called when the application calls WD_KernelPlugInIntDisable().
funcIntAtIrql - This is the Kernel Mode interrupt handler. This call-back function is called when the WinDriver processes the interrupt which is assigned to this Kernel PlugIn. If this function returns a value greater than 0, then funcIntAtDpc is called as a Deferred procedure call.
funcIntAtDpc - Most of your interrupt handler code should be written in this call-back. It is called as a Deferred procedure call, if the funcIntAtIrql returns a value greater than 0.

TYPENAMEDESCRIPTION
KP_FUNC_CLOSEfuncCloseName of your KP_Close() function in the kernel.
KP_FUNC_CALLfuncCallName of your KP_Call() function in the kernel.
KP_FUNC_INT_ENABLE funcIntEnable Name of your KP_IntEnable() function in the kernel.
KP_FUNC_INT_DISABLEfuncIntDisableName of your KP_IntDisable() function in the kernel.
KP_FUNC_INT_AT_IRQL funcIntAtIrql Name of your KP_IntAtIrql() function in the kernel.
KP_FUNC_INT_AT_DPCFuncIntAtDpcName of your KP_IntAtDpc() function in the kernel.

Chapter 18
Developing in Visual Basic and Delphi

The entire WinDriver API can be used when developing drivers in Visual Basic and Delphi.

Using DriverWizard

DriverWizard can be used to diagnose your hardware and verify that it is working properly before you start coding. DriverWizard's automatic source code generator generates code in C and Delphi only. Automatic generation of code in Visual Basic will be supported in later versions of WinDriver.

To create your driver code in C and Delphi, please refer to the ``DriverWizard'' 4 chapter.

Samples

Samples for drivers writtten using the WinDriver API in Delphi or Visual Basic can be found in :

  1. \windriver\delphi\samples
  2. \windriver\vb\samples

Use these samples as a starting point for your own driver.

Kernel PlugIn

Delphi and Visual Basic cannot be used to create a Kernel PlugIn. Developers using WinDriver with Delphi or VB in User Mode, must use C when writing their Kernel PlugIn.

Creating your Driver

The method of development in Visual Basic is the same as the method in C except for the automatic code generation feature of DriverWizard.

Your work process should be as follows:

  1. windrvr.cls
  2. windrvr_usb.cls

  1. windrvr.pas
  2. windrvr_usb.pas

Chapter 19
Trouble-Shooting

To determine and verify the cause of your driver problems ¡ Open the DebugMonitor and set your desired trace level. This will help narrow down your debugging process and lead you in the right direction.

19.1  WD_Open() (or xxx_Open()) fails.

The following may cause WD_Open() to fail:

19.2  WD_CardRegister() fails

WD_CardRegister fails if one of the resources defined in the card cannot be locked.

First, check out what resource (out of all the card's resources) cannot be locked.

Activate the KernelTracer and set the trace mode to trace. This will output all warning and error debug messages. Now, run your application and you will get a printout of the resource that failed.

After finding out the resource that cannot be locked, check out the following:

Is the resource in use by another application? In order for several resource lock requests to the same I/O, Memory or interrupt to succeed, both applications must enable sharing of the resource. This is done by setting fNonSharable = FALSE for every item that can be shared.

19.3  Can't open USB device using the DriverWizard

When a driver already exists in Windows for your device, you must create an .INF file (DriverWizard automates this process) and install it. For exact instructions, see the sections explaining how to create and install .INF files.

19.4  Can't get interfaces for USB devices.

In some operating systems (such as Windows 98), when there is no driver installed for your USB device (Symptom - In DriverWizard's ``Card Information'' screen, the device's physical address is 0x0.), you must create an .inf file (DriverWizard automates this process) and install it. For exact instructions, see the sections explaining how to create and install .INF file.

19.5  PCI Card has no resources when using the DriverWizard

In some operating systems (such as Windows 98), when there is no device driver for a new device, the operating system does not allocate resources to the device.
The symptom ¡- When trying to open the card in DriverWizard's ``Card Information'' screen, a message pops-up notifying that no resources were found on card. In addition, card configuration registers, such as memory `BAR' are zeroed. When this happens, you need to create and install an INF file for the new card. For exact instructions,see Chapters 4 and 21 that explain how to create and install a .INF file.

19.6  Computer hangs on interrupt

This can occur with level-sensitive interrupt handlers. PCI cards interrupts are usually level sensitive.

Level sensitive interrupts are generated as long as the physical interrupt signal is high. If the interrupt signal is not lowered by the end of the interrupt handling by the kernel, the Windows OS will call the WinDriver kernel interrupt handler again - This will cause the PC to hang!

Acknowledging a level sensitive interrupt is hardware specific. Acknowledging an interrupt means lowering the interrupt level generated by the card. Normally, writing to a register on the PCI card can terminate the interrupt, and lower the interrupt level.

When calling WD_IntEnable() it is possible to give the WinDriver kernel interrupt handler a list of transfer commands (IO and memory read/write commands) to perform upon interrupt, at the kernel level - before WD_IntWait() returns.

These commands can be used to write to the needed register to lower the interrupt level, thereby `re-setting' the interrupt.

Before calling WD_IntEnable(), prepare two transfer command structures (to read the interrupt status and then write the status to lower the level).

 WD_TRANSFER trans[1];

 BZERO (trans);

 trans[0].cmdTrans = WP_DWORD; // Write Port Dword

// address of IO port to write to

 trans[0].dwPort = dwAddr;

 // the data to write to the IO port

 trans[0].Data.Dword = 0;

 Intrp.dwCmds = 1;

 Intrp.Cmd = trans;

 Intrp.dwOptions = INTERRUPT_LEVEL_SENSITIVE;

 WD_IntEnable(hWD, &Intrp);

This will tell WinDriver's kernel to Write to the register at dwAddr a value of `0', upon an interrupt.

The user-mode interrupt handler is the thread waiting on WD_IntWait() - this is your code. Here you only do your normal stuff to handle the interrupt. You do not need to clear the interrupt level since this is already done by the WinDriver kernel , with the transfer command you gave WD_IntEnable().

WD_DMALock() fails to allocate buffer

The efficient method for memory transfer is scatter/gather DMA. If your hardware does not support scatter/gather, you will need to allocate a DMA buffer using WD_DMALock().

WD_DMALock() fails when the Windows OS has run out of contiguous physical memory.

When calling WD_DMALock() with dwOptions = DMA_KERNEL_BUFFER_ALLOC, WinDriver requests the Windows OS for a physical contiguous memory block.

On WinNT you can allocate a few hundred kilobytes by default. If you want to allocate a few megabytes, you will have to reserve memory for it, by setting the following value in the registry:

On Windows NT:

Run REGEDIT.EXE, and access the following key:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control \SessionManager \MemoryManagement

Increment the value of NonPagesPoolSize.

This change will take place only after re-boot.

On Windows 95:

Win95 does not support contiguous buffer reservation, therefore, the earlier you allocate the buffer, the larger the block you can allocate.

Chapter 20
Dynamically loading your driver

20.1  Windows NT/2000 and 95/98/ME

20.1.1  Dynamic loading - background

When adding a new driver to the Windows operating system, you must re-boot the system, for the Windows to load your new driver into the system. Dynamic loading enables you to install a new driver to your operating system, without needing to re-boot. WinDriver is a dynamically loadable driver, and provides you with the utility needed to dynamically load the driver you create. You may dynamically load your driver whether you have created a User Mode or a Kernel Mode driver.

20.1.2  Why do you need a dynamically loadable driver?

A dynamically loadable driver enables your customers to start your application immediately after installing it, without the need to re-boot.

20.1.3  Dynamically loading and unloading your driver

The utility you use to dynamically load and unload your driver is called WDREG.EXE, and is found in \windriver\util\WDREG.EXE.

USAGE: WDREG [-vxd ] [-name <driver name>] [-file <driver file name>]
[[CREATE] [START] [STOP] [DELETE] [INSTALL] [REMOVE]]

WDREG.EXE has 4 basic operations:

  1. CREATE - Instructs the Windows to load your driver next time it boots, by adding your driver to the registry.
  2. START - Dynamically loads your driver into memory for use. On Windows NT/2000, you must first `CREATE' your driver before `START'ing it.
  3. STOP - Dynamically unloads your driver from memory.
  4. DELETE - Removes your driver from the registry , so that it does not load on next boot.

For example, to reload WinDriver use:

WDREG STOP START

WDREG.EXE has 2 `shortcut' operations for your convenience:

INSTALL - Creates and starts your driver (same as using WDREG CREATE START).

REMOVE - Unloads your driver from memory, and removes it from the registry so that it does not load on next boot (same as using WDREG STOP DELETE).

You may dynamically load your driver via command line or from within your application as follows:

Dynamically loading your driver via command line:

From the command line, type WDREG INSTALL. This loads the driver into memory, and instructs Windows to load your driver on the next boot.

Dynamically loading your driver in your installation application:

Add the WDREG source code to your installation application.

The full source code for WDREG is found in \windriver\samples\wdreg\

For Linux and Solaris, use the command wdreg <driver name> to load and unload your driver. The parameters are the same as that of the Windows version.

20.1.4  Dynamically loading your Kernel PlugIn

If you have used WinDriver to develop a Kernel PlugIn, you must dynamically load your Kernel PlugIn as well as the WinDriver.

To Dynamically load / unload your Kernel PlugIn driver ([Your driver name].VXD / [Your driver name].SYS):

Use the WDREG command as described above, with the addition of the ''- name'' flag, after which you must add the name of your Kernel PlugIn driver.

For example, to load your Kernel PlugIn driver called KPTest.VXD or KPTest.SYS, use:

WDREG -name KPTest install

(You should not add the .VXD or .SYS extension to your driver name).

WDREG allows you to install your driver in the registry under a different name than the physical file name.

USAGE: WDREG -name [Your new driver name] -file
[Your original driver name] install

For example, typing the following:

WDREG -name ``Kernel PlugIn Test'' -file KPTest install

Installs the KPTest.VXD or KPTest.SYS driver under a different name.

20.2  Linux

To dynamically load WinDriver on Linux, execute:

> /sbin/insmod -¡f /lib/modules/misc/windrvr.o

To dynamically unload WinDriver, execute:

> /sbin/rmmod windrvr

In addition, you can use the wdreg script under Linux to install (load) windrvr.o

20.3  Solaris

To dynamically load WinDriver on Solaris, execute:

> /usr/sbin/add_drv -m ``* 0666 root sys'' windrvr

To dynamically unload WinDriver, execute:

> /usr/sbin/rem_drv windrvr

In addition, you can use the wdreg script under Solaris to install (load) windrvr.o

Chapter 21
Distributing your Driver

Read this chapter in the final stages of driver development. This chapter guides you in creating your driver package for distribution.

21.1  Get a valid license for your WinDriver

To purchase your WinDriver license, fill in your order form found in \windriver\docs\order.txt, and fax or email it to Jungo (you can find the full details on the order form itself).

Alternatively, you can order WinDriver on-line. See Jungo's WEB site http://www.jungo.com for more details.

When you receive the registered string, you should follow the instructions found under \windriver\redist\register.txt to enable your driver to work with the registered version of the WinDriver kernel. This applies for all OS'es.

21.2  Windows 95/98/ME and NT/2000

  1. Copy VxD or SYS files to the target computer.

    In the driver installation script you create, you must copy the following files to the target computer (the system on which you want to install your driver):

    USB developers:

    If you have created a Kernel Plugin

  2. Add WinDriver to the list of Device Drivers Windows loads on boot.

    This is done by calling WDREG install. You can add the WDREG source code (found in \windriver\samples\wdreg\wdreg.cpp) to your own installation code, in order to install WinDriver programmatically. The utility WDREG.EXE is found it the UTIL subdirectory under the WinDriver installation.

    For Windows 98/ME, WDREG.EXE install installs WINDRVR.SYS and requires a reboot. For a reboot free installation on Windows 98/ME, please install WINDRVR.VXD instead and use the command WDREG -vxd install to install it.

    If you have created a WinDriver Kernel PlugIn as well, call ``WDREG.EXE -name [Your driver name] install''. You can add the `WDREG' source code (found in \windriver\samples\wdreg\wdreg.cpp) to your own installation code, in order to install a kernel plugin.

NOTE: When distributing your driver, take care to see that you DO NOT overwrite a newer version of windrvr.sys in the windows\system32\drivers directory or windrvr.vxd in the windows\system\vmm32 directory with an older version of the file. You should configure your Install Shield installation program (if you are using Install Shield) or your .INF file such that the installer automatically compares the timestamp on these two files and DOES NOT overwrite a newer version with an older one.

21.3  Creating a .INF file

Device information (INF) files are text files, that provide information used by the ``Plug and Play'' mechanism in Windows ME/95/98/2000 to install software that supports a given hardware device. INF files are required for hardware that identifies itself, such as USB and PCI. The INF file includes all necessary information about the device(s) and the files to be installed. When hardware manufactures introduce new products, they must create INF files to explicitly define the resources and files required for each class of device.

In some cases, the .INF file of your specific device is included in the .INF files supplied with the operating system. In other cases, you will need to create a .INF file for your device. DriverWizard can generate an INF specific for your Card/device. The INF is used to tell the OS that the selected device is now handled by WinDriver.

21.3.1  Why should I create an INF file?

  1. To stop the `new hardware wizard' of the Windows operating system from popping up after boot.
  2. In some cases the OS doesn't initialize the PCI configuration registers in Win98/ME without an INF file.
  3. In some cases the OS doesn't assign physical address to USB devices without an INF file.
  4. To load the new driver created for the card / device. Creating an INF file is required whenever developing a new driver for the hardware.
  5. To replace the existing driver with a new one.

21.3.2  How do I install an INF file when no driver exists?

  1. When no driver exists for your hardware, use DriverWizard to generate an INF file for your card / device (the INF file includes your device VID/PID and loads WDPNP.SYS as your device driver).
  2. Save the file under C:\temp\mydevice.INF (or any other name or location you choose). For instructions on how to generate the INF file, see Chapter 4 that explains the DriverWizard .
  3. Go to: Start | Settings | Control Panel | System.
  4. Use the operating system `Add new hardware' wizard to add and register the .INF file created with WinDriver. In the relevant screen enter the path of the new .INF file created with WinDriver.

21.3.3  How do I replace an existing driver using the INF file?

21.3.3.1  Windows 2000

  1. Use DriverWizard to generate an INF file for your card / device (the INF file includes your device VID/PID and loads WDPNP.SYS as your device driver).
  2. Save the file under C:\temp\mydevice.INF (or any other name or location you choose). For instructions on how to generate the INF file, see Chapter 4 that explains the DriverWizard.
  3. Go to: Start | Settings | Control Panel | System.
  4. Click the `Device Manager' option in the ``Hardware'' tab.
  5. Now select the View Devices by connection option.
  6. For PCI cards, navigate to Standard PC | PCI bus | <your_card>.
  7. For USB devices: navigate to Standard PC | PCI bus | PCI to USB Universal Host Controller | USB Root Hub | <the current driver for the device>.
  8. Select Action | Properties.
  9. Click the `Update Driver' button in the ``Driver'' tab.
  10. In DriverWizard: click on the `Next' button.
  11. Select ``Search for a suitable driver for my device'' option by clicking on the appropriate radio button.
  12. Click the ``Next'' button
  13. Check the ``only Specify a location'' check-box
  14. Click the ``Next'' button
  15. Enter the path: c:\temp\<your .INF file> in the input box that is displayed.
  16. Click the ``OK'' button
  17. Click the ``Next'' button
  18. Click the ``Finish'' button
  19. Reboot

21.3.3.2  Windows 95/98/ME

  1. Use DriverWizard to generate an INF file for your card / device (the INF file includes your device VID/PID and loads WDPNP.SYS as your device driver).
  2. Save the file under C:\temp\mydevice.INF (or any other name or location you choose). For instructions on how to generate the INF file, see Chapter 4 that explains the DriverWizard.
  3. Go to: Start | Settings | Control Panel | System.
  4. Now select the View Devices by connection option.
  5. For PCI cards, navigate to Standard PC | PCI bus | <your_card>.
  6. For USB devices: navigate to Standard PC | PCI bus | PCI to USB Universal Host Controller | USB Root Hub | <the current driver for the device>.
  7. Select Properties.
  8. Click the `Update Driver' button in the ``Driver'' tab.
  9. Click the ``Next'' button
  10. Check the ``only Specify a location'' check-box
  11. Click the ``Next'' button
  12. Enter the path: c:\temp\<your .INF file> in the input box that is displayed.
  13. Click the ``OK'' button
  14. Click the ``Next'' button
  15. Click the ``Finish'' button
  16. Reboot

21.4  Windows CE

Copy WinDriver Kernel DLL file to the target computer

In the driver installation script you create, you must copy the following files to the target computer (the one you will install your driver on):

For Windows CE hand-held computer installations:

Copy WINDRVR.DLL file to \WINDOWS on your target Windows CE computer.

For Windows CE PC:

Copy WINDRVR.DLL %_FLATRELEASEDIR% and use MAKEIMG.EXE to build a new Windows CE kernel NK.BIN. You should modify PLATFORM.REG and PLATFORM.BIB appropriately before doing this by appending the contents of the supplied files PROJECT_WD.REG and PROJECT_WD.BIB respectively. This process is similar to the process of installing WinDriver CE on a CE PC /ETK installation as described in INSTALLATION AND SETUP.

Add WinDriver to the list of Device Drivers Windows CE loads on boot

For Windows CE hand-held computer installations, please modify the registry according to the entries documented in the file PROJECT_WD.REG. This can be done using the Windows CE Pocket Registry Editor on the hand-held CE computer or by using the Remote CE Registry Editor Tool supplied with the Windows CE Platform SDK. You will need to have Windows CE Services installed on your Windows NT Host System to use the Remote CE Registry Editor Tool. For Windows CE PC/ETK, the required registry entries are made by appending the contents of the file PROJECT_WD.REG to the Windows CE ETK configuration file PROJECT.REG before building the Windows CE image using MAKEIMG.EXE. If you wish to make the WinDriver kernel file a permanent part of the Windows CE kernel NK.BIN, you should append the contents of the file PROJECT_WD.BIB to the Windows CE ETK configuration file PROJECT.BIB as well.

21.5  Linux

The Linux kernel is continuously under development, and kernel data structures are subject to frequent change. To support such a dynamic development environment and still have kernel stability, the Linux kernel developers decided that kernel modules must be compiled with the identical header files that the kernel itself was compiled with. They enforce this by #include'ing a version number into the kernel header files that is checked against the version number encoded into the kernel. This forces Linux driver developers to facilitate recompilation of their driver based on the target system's kernel version.

21.5.1  WinDriver Kernel Module

Since Windrvr.o is a kernel module, it requires recompilation for every kernel version that it must be loaded on. To faciliate this, we supply the following components to insulate the windriver kernel module from the Linux kernel:

You need to distribute these components along with your driver source code or object code. We suggest that you adapt our makefile from the windriver/redist directory to compile and insert the module windrvr.o into the kernel. Note that this makefile calls the wdreg utility shell script that we supply under windriver/util. You should understand how this works and adapt it for your own needs.

21.5.2  Your User Mode Driver

Since the user mode driver does not have to matched against the kernel version number, you are free to distribute it as binary code (in case you wish to protect your source code from unauthorized copying), or as source code.

21.5.3  Kernel Plugin Modules

Since the kernel plugin module is a kernel module, it also needs to be matched against the active kernel's version number. This means recompilation for the target system. It is advisable to supply the kernel plugin module source code to clients so that they may recompile it. You may also use the same makefile, to build and insert any kernel plugin modules that you distribute with Linux, that you use to recompile and install the WinDriver kernel module.

21.5.4  Installation script

We suggest that you supply an installation shell script that copies your driver executables to the correct places (perhaps /usr/local/bin), then invoke make or gmake to build and install the windriver kernel module and any kernel plugin modules.

21.6  Solaris

For Solaris, you need to supply the following items to enable the client to enable target installation of your driver:

21.6.1  Installation script

We suggest that you supply an installation shell script that copies your driver executables to the correct places (perhaps /usr/local/bin), then install the windriver kernel and any kernel plugin modules. You may adapt the utility scripts wdreg and install_windrvr, that we supply under the directory windriver/util for your purpose.

21.7  VxWorks

For VxWorks, you need to supply the following items to enable the client to enable target installation of your driver:

The client that you provide these modules to, would want to incorporate all these files into the VxWorks embedded image. There are two steps involved here:

  1. windrvr.o and your_drv.out has to be built into the VxWorks image.

    In the Tornado II Project's build specification for the VxWorks image, specify windrvr.o and your_drv.out as EXTRA_MODULES under the MACROS tab, and copy these files under the appropriate target directory tree. Rebuild the project and these files are now included in the image and it should work.

  2. During startup, the drvrInit() routine should be called to initialize windrvr.o Your driver's startup routine may also need to be called.

    You have to use the file usrAppInit.c found under the Tornado II project directory and insert code to call drvrInit() - which is WinDriver's initialization routine - and your driver applications startup routine. Of course, this means you need to rebuild the VxWorks image.

Appendix A
PC-Based Development Platform Parallel Port Cable Info

To use the parallel port shell utility (Ppsh) to transfer a Windows CE image from your development workstation to a PC-based hardware development platform, a custom parallel cable is required. This cable requires a DB-25 male connector at both ends, with pins mapped as follows:

To order this cable, please contact:

Redmond Cable

15331 NE 90th Street

Redmond, WA 98052

Telephone: (425) 882-2009

Fax: (425) 883-1430

Part Number: 64355913

Appendix B
Limitations on demo versions

Windows 95/98/ME and NT/2000

Windows CE

Linux

Solaris

VxWorks

DRIVERWIZARD GUI

Appendix C
Version history list

New in Version 2.02

New in Version 2.10

New in Version 2.11

New in Version 2.12

New in Version 3.0

New in Version 3.01

New in Version 3.02

New in Version 3.03

New in Version 4.0

New in Version 4.1

New in Version 4.14

New in Version 4.20

New in Version 4.30

New in version 4.31

New in version 4.32

New in Version 4.33

New in Version 4.34

New in Version 5.0

Appendix D
Purchasing WinDriver

Choose the WinDriver product that suits your needs:

Fill in the order form found in `Start | WinDriver | Order Form' on your Windows start menu, and send it back to Jungo via email/fax/mail (see details below).

Your WinDriver package will be sent to you via Fedex / Postal mail. The WinDriver license string will be emailed to you immediately.

E - M A I L

Support: support@jungo.com

Sales: sales@jungo.com

Services: services@jungo.com

P H O N E / F A X

Phone:

USA (Toll-Free): 1-877-514-0537

Worldwide: +972-9-8859365

Fax:

USA (Toll-Free): 1-877-514-0538

Worldwide: +972-9-8859366

W E B:

Jungo http://www.jungo.com

POSTAL A D D R E S S

Jungo Ltd,

P.O.Box 8493,

Netanya 42504,

ISRAEL

Appendix E
Distributing your driver - legal issues

WinDriver is licensed per-seat. The WinDriver license allows one developer on a single computer to develop an unlimited number of device drivers, and to freely distribute the created driver without royalties, as outlined in the license agreement below.

SOFTWARE LICENSE AGREEMENT OF WinDriver V5.0

Jungo © 1999-2001

JUNGO (``LICENSOR'') IS WILLING TO LICENSE THE ACCOMPANYING SOFTWARE TO YOU ONLY IF YOU ACCEPT ALL OF THE TERMS IN THIS LICENSE AGREEMENT. PLEASE READ THE TERMS CAREFULLY BEFORE YOU INSTALL THE SOFTWARE, BECAUSE BY INSTALLING THE SOFTWARE YOU ARE AGREEING TO BE BOUND BY THE TERMS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO THESE TERMS, LICENSOR WILL NOT LICENSE THIS SOFTWARE TO YOU, AND IN THAT CASE YOU SHOULD IMMEDIATELY DELETE ALL COPIES OF THIS SOFTWARE YOU HAVE IN ANY FORM.

OWNERSHIP OF THE SOFTWARE

  1. The enclosed Licensor software program (``Software'') and the accompanying written materials are owned by Licensor or its suppliers and are protected by United States of America copyright laws, by laws of other nations, and by international treaties.

    GRANT OF LICENSE

  2. Jungo grants to you as an individual, a personal, non-exclusive ``one-user'' license to use the Software on a single computer in the manner provided below at the site for which the license was given. If you are an entity, Jungo grants you the right to designate one individual within your organization to have the right to use the Software on a single computer in the manner provided below at the site for which the license was given.

  3. If you have not yet purchased a license to the Software, Licensor grants to you the right to use the Software for an evaluation period of 30 days. If you wish to continue using the Software and accompanying written materials after the evaluation period, you must register the Software by sending the required payment to Licensor. You will then receive a license for continued use and a registration code that will permit you to use the Software on a single computer free of payment reminders. The Software may come with extra programs and features that are available for use only to registered users through the use of their registration code.

    RESTRICTIONS ON USE AND TRANSFER

  4. You may not distribute any of the headers or source files which are included in the Software package.

  5. The license for WinDriver allows you for royalty free distribution of the following files only when complying with 5a, 5b, 5c and 5d of this agreement: WINDRVR.SYS (Windows NT), WINDRVR.VXD (Windows 95/98/ME), WINDRVR.DLL (Windows CE), WDPNP.SYS(98/ME/2000),windrvr.o (Linux) - as generated from `make install', windrvr and windrvr.cnf (Solaris), and windrvr.o (Vxworks).


    5a. These files may be distributed only as part of the application you are distributing, and only if they significantly contribute to the functionality of your application.


    5b. You may not distribute the WinDriver header file (WINDRVR.H). You may not distribute any header file which describes the WinDriver functions, or functions which call the WinDriver functions and have the same basic functionality as the WinDriver functions themselves.


    5c. You may not modify the distributed files specified in section 5 of this agreement.


    5d. WinDriver may not be used to develop a development product, an API, or any products which will eventually be part of a development product or environment, without the written consent of the licensor.

  6. You may make printed copies of the written materials accompanying Software provided that they used only by users bound by this license.

  7. You may not distribute or transfer your registration code or transfer the rights given by the registration code.

  8. You may not rent or lease the Software or otherwise transfer or assign the right to use the Software.

  9. You may not reverse engineer, decompile, or disassemble the Software.

    DISCLAIMER OF WARRANTY

  10. THIS SOFTWARE AND ITS ACCOMPANYING WRITTEN MATERIALS ARE PROVIDED BY LICENSOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT, ARE DISCLAIMED.

  11. IN NO EVENT SHALL LICENSOR OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, SAVINGS, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Because some states do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you.

  12. This Agreement is governed by the laws of the United States of America.

  13. If you have any questions concerning this Agreement or wish to contact the Licensor for any reason, please write to:

    Jungo © 1999-2001

    Address:

    Jungo Ltd,

    P.O.Box 8493

    Netanya 42504

    ISRAEL.

    Web site:

    http://www.jungo.com

    E-mail:

    info@jungo.com

    Voice:

    1-877-514-0537(USA)

    +972-9-8859365(Worldwide)

    Fax:

    1-877-514-0538(USA)

    +972-9-8859366(Worldwide)

    U.S. GOVERNMENT RESTRICTED RIGHTS

  14. The Software and documentation are provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the Government is subject to restrictions set forth in subparagraph (c)(1) of The Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or sub-paragraphs (c)(1)(ii) and (2) of Commercial Computer Software - Restricted Rights at 48 CFR 52.227-19, as applicable.